mysql实战优化之五: 更新/插入优化 sql优化
通常情况下,当访问某张表的时候,读取者首先必须获取该表的锁,如果有写入操作到达,那么写入者一直等待读取者完成操作(查询开始之后就不能中断,因此允许读取者完成操作)。当读取者完成对表的操作的时候,锁就会被解除。如果写入者正在等待的时候,另一个读取操作到达了,该读取操作也会被阻塞(block),因为默认的调度策略是写入者优先于读取者。当第一个读取者完成操作并解放锁后,写入者开始操作,并且直到该写入者完成操作,第二个读取者才开始操作。
通过LOCK TABLES和UNLOCK TABLES语句可以显式地获取或释放锁,但是在通常情况下,服务器的锁管理器会自动地在需要的时候获取锁,在不再需要的时候释放锁。获取的锁的类型依赖于客户端是写入还是读取操作。
对某张表进行写入操作的客户端必须拥有独占的(排他的)访问权的锁。操作在进行的过程中,该数据表处于不一致的(inconsistent)状态,因为数据记录在删除、添加或修改的时候,数据表上的索引也可能需要更新以相互匹配。允许两个客户端同时写入一张数据表是不利的,因为这样的操作会很快使数据表中的信息成为一堆无用的垃圾。同时允许客户端读取变化之中的数据表也不正确,因为正在读取的位置中的数据可能正在变化(修改),读取的结果可能并不是真实的。因此对某张表执行读取操作的客户端也必须获取一个锁,防止在读取的过程中,其它的客户端写入或改变表。但是这个锁不需要独占的访问权。因为读取操作不会改变数据,因此没有理由让某个读取者阻止其它的读取者访问这张表。故读取锁可允许其它的客户端在同一时刻读取这张表。
虽然通过锁机制,可以实现多线程同时对某个表进行操作,但当某个线程作更新操作时,首先要获得独占的访问权。在更新的过程中,所有其它想要访问这个表的线程必须要等到其更新完成为止。此时就会导致锁竞争的问题,从而导致用户等待时间的延长。
要提高MySQL的更新/插入效率,应首先考虑降低锁的竞争,减少写操作的等待时间。
1.同时插入多行记录时,宜使用多个值表的INSERT 语句
如果可以同时从同一客户插入很多行时,宜使用多个值表的INSERT 语句。多个值表的 INSERT 语句 ,可以大大缩减客户端与数据库之间的连接、语法分析等消耗,使得效率比分开执行的单个 INSERT 语句快很多。
如批量插入:
INSERT INTO tb (fa, fb, fc) VALUES ('1', '12', '13'), ('2', '22', '23'), ('3', '32', '33'),
多值的 INSERT语句还可以通过调整 bulk_insert_buffer_size 参数来提高数据插入的效率,这个参数设置的是 bulk insert 的缓存大小,默认是 8M 。注意,这只能对myisam表使用。
3)可以对myisam表并行插入Concurrent_insert系统变量可以被设置用于修改concurrent-insert处理。该变量默认设置为1。如果concurrent_insert被设置为0,并行插入就被禁用。如果该变量被设置为2,在表的末端可以并行插入,即便该表的某些行已经被删除。
2.考虑使用replace 语句代替insert语句
根据应用情况可以使用replace 语句代替insert/update语句。例如:如果一个表在一个字段上建立了唯一索引,当向这个表中使用已经存在的键值插入一条记录,将会抛出一个主键冲突的错误。如果我们想用新记录的值来覆盖原来的记录值时,就可以使用REPLACE语句。
使用REPLACE插入记录时,如果记录不重复(或往表里插新记录),REPLACE功能与INSERT一样,如果存在重复记录,REPLACE就使用新记录的值来替换原来的记录值。使用REPLACE的最大好处就是可以将DELETE和INSERT合二为一,形成一个原子操作。这样就可以不必考虑同时使用DELETE和INSERT时添加事务等复杂操作了。
在使用REPLACE时,表中必须有唯一有一个PRIMARY KEY或UNIQUE索引,否则,使用一个REPLACE语句没有意义。
mysql replace语句:
用法1:replace into:
replace into table (id,name) values('1','aa'),('2','bb')
此语句的作用是向表table中插入两条记录。如果主键id为1或2不存在就相当于插入语句:
insert into table (id,name) values('1','aa'),('2','bb')
如果存在相同的值则不会插入数据。
用法2:replace(object, search, replace)
作用是把object中出现search的全部替换为replace,例:
select replace(‘abc’, ‘b’, ‘x’);
例:把表table中的name字段中的aa替换为bb
update table set name=replace(name,'aa','bb')
注意:UPDATE和REPLACE的区别:
1)UPDATE在没有匹配记录时什么都不做,而REPLACE在有重复记录时更新,在没有重复记录时插入。
2)UPDATE可以选择性地更新记录的一部分字段。而REPLACE在发现有重复记录时就将这条记录彻底删除,再插入新的记录。也就是说,将所有的字段都更新了。
3.在插入大量数据之前,可以先将表锁定(Lock Tables)
为了提高数据插入的效率,可以考虑在插入之前先将表锁定。这主要是因为直到所有的INSERT语句都完成之后,索引缓存一次性刷新到磁盘中。通常情况下,有多少次INSERT语句就会有多少次索引缓存刷新到磁盘中的开销。为此在数据插入之前,将数据表进行锁定,就可以大幅度的提高数据插入的效率。当然,如果你可以用一个插入语句实现所有行的插入,则无需使用显式锁定语句。(针对非事务性表)
因此如果一个表的更新频率比较高时,那么可以使用Lock Tables选项来提高更新速度。
对于事务性表,要想更快地进行表插入,可以使用START TRANSACTION和COMMIT语句代替LOCK TABLES来提高更新速度。
4.可以对myisam表并行插入Concurrent_insert系统变量可以被设置用于修改concurrent-insert处理。
该变量默认设置为1。如果concurrent_insert被设置为0,并行插入就被禁用。如果该变量被设置为2,在表的末端可以并行插入,即便该表的某些行已经被删除。
5.使用插入延迟
如果客户无需等待插入完成的时候(即用户对插入数据的即时性要求可能并不是很高),此时就可以考虑采用插入延迟特性。Delayed的含义是让insert语句马上执行并返回,而数据被放在内存的队列中等待被插入,并没有真正的写入磁盘;这比每条语句都分别插入要快的多。
使用插入延迟的另一个好处就是从多个客户插入的情况会被绑定并记录在同一个block中。
默认情况下,在MySQL数据库中,更新操作比Select查询有更高的优先级。MySQL的默认的调度策略可用总结如下:
• 写入操作优先于读取操作。
• 对一张数据表的写入操作同一时刻只能发生一次,写入请求按照它们到达的次序来处理。
• 对一张数据表的多个读取操作可以同时地进行。
MySQL允许改变语句调度的优先级,它可以使来自多个客户端的查询更好地协作,这样单个客户端就不会由于锁定而等待很长时间。改变优先级还可以确保特定类型的查询被处理得更快。通过以下三种方式来修改它的调度策略:
• LOW_PRIORITY关键字应用于DELETE、INSERT、LOAD DATA、REPLACE和UPDATE。这个属性可以将某个特定的语句的优先级降低。如可以调低某个特定的更新语句或者插入语句的优先级。不过需要注意的是,这个属性只有对特定的语句有用。即其作用域只针对某个特定的语句,而不会对全局造成影响。
例:UPDATE [LOW_PRIORITY] tbl_name SET col_name1=expr1,col_name2=expr2,...
mysql中update用low_priority让update不锁定表
• HIGH_PRIORITY关键字应用于SELECT和INSERT语句。这个属性可以用来提高某个特定的Select查询语句的优先级。LOW_PRIORITY刚好相反,在所有其他用户对表的读写完成后才进行插入。这里需要注意,跟上面这个属性一样,这个作用域也只限于特定的查询语句。而不会对没有加这个参数的其他查询语句产生影响。也就是说,其他查询语句如果没有加这个属性,那么其优先级别仍然低于更新进程。
• DELAYED关键字应用于INSERT和REPLACE语句。
LOW_PRIORITY和HIGH_PRIORITY调节符影响那些使用数据表锁的存储引擎(例如MyISAM和MEMORY)。DELAYED调节符作用于MyISAM和MEMORY数据表。
通常情况下,某张数据表正在被读取的时候,如果有写入操作到达,那么写入者一直等待读取者完成操作(查询开始之后就不能中断,因此允许读取者完成操作)。如果写入者正在等待的时候,另一个读取操作到达了,该读取操作也会被阻塞(block),因为默认的调度策略是写入者优先于读取者。当第一个读取者完成操作的时候,写入者开始操作,并且直到该写入者完成操作,第二个读取者才开始操作。
如果写入操作是一个LOW_PRIORITY(低优先级)请求,那么系统就不会认为它的优先级高于读取操作。在这种情况下,如果写入者在等待的时候,第二个读取者到达了,那么就允许第二个读取者插到写入者之前。只有在没有其它的读取者的时候,才允许写入者开始操作。理论上,这种调度修改暗示着,可能存在LOW_PRIORITY写入操作永远被阻塞的情况。如果前面的读取操作在进行的过程中一直有其它的读取操作到达,那么新的请求都会插入到LOW_PRIORITY写入操作之前。
SELECT查询的HIGH_PRIORITY(高优先级)关键字也类似。它允许SELECT插入正在等待的写入操作之前,即使在正常情况下写入操作的优先级更高。另外一种影响是,高优先级的SELECT在正常的SELECT语句之前执行,因为这些语句会被写入操作阻塞。
如果希望某一连接支持LOW_PRIORITY选项来处理,那么通过Set LOW_PRIORIT_UPDATES=1来设置连接变量,通过这个设置可以制定具体连接中的所有更新进程都是用比较低的优先级。但注意这个选项只针对特定的连接有用。对于其他的连接,就不适用。
如果希望所有支持LOW_PRIORITY选项的语句都默认地按照低优先级来处理,那么请使用--low-priority-updates选项来启动服务器。采用这个选项启动数据库时,系统会给数据库中所有的更新语句比较低的优先级。通过使用INSERT HIGH_PRIORITY来把INSERT语句提高到正常的写入优先级,可以消除该选项对单个INSERT语句的影响。
6.使用LOAD DATA INFILE从文本下载数据将比使用插入语句快20倍。
Load Date Infile是从一个文件中导入数据。如果采用这种方式的话,用户需要预先准备一个固定格式的文件。如果插入的数据量比较多,例如软件运行环境配置时可能会导入大量预配置数据时,此时使用Load Date Infile的方式能够取得比较好的性能。
执行LOAD DATA INFILE,数据插入到表中,由于无需更新表索引,因此这将非常快。
7.将大表分为多个小表来降低锁竞争
一些大表或频繁更新的表中,由于表的访问量较大,因此锁竞争也比较严重。此时,可以人为地将表合理分为多个小表,使表访问分散到多张表上,相互之间不会产生干扰时,就会降低表上锁的竞争,从而提高了访问效率。而当需要访问完整数据时,可以通过视图进行整合成一张表。