标签:
1、锁机制
1)在处理并发读写时,通常使用一套锁系统来解决问题。锁系统由读锁(又称共享锁)和写锁(又称排他锁)组成。
2)每一种锁操作(如获得锁、检查锁是否已解除、释放锁),都会增加系统开销。锁策略就是在锁开销和数据安全之间寻求一种平衡。MySQL的每种存储引擎都可以实现独有的锁策略或锁粒度。在存储引擎设计中,锁管理是个非常重要的议题。两种最重要的锁策略:
(1)行级锁:可以支持最大的并发处理能力,但也带来最大的锁开销。行级锁由存储引擎(如InnoDB)实现,服务器完全不了解存储引擎里的锁实现方式。
(2)表级锁:开销最小。
由于通常认为表更新比表检索更重要,因此写锁比读锁有更高的优先级。
虽然存储引擎管理自己的锁,但MySQL本身也能使用各种有效的表级锁,如MySQL服务器在ALTER TABLE语句中使用表级锁,而不用考虑存储引擎。
以下的内容仅用于讨论MyISAM(如下图)。对于使用InnoDB存储引擎的表,读者可以自行测试。
使用表级锁的时候,读写一般是串行的。但使用READ LOCAL表级锁可以支持某种类型的并发写操作,如下面的图文说明:
I、此时concurrent_insert变量为1,且表没有出现空洞(自表创建以来,还没有删除操作)。如图,在session 1中对表使用read local表级锁,在session 2中可以执行插入操作,但session 1要unlock tables之后才能看到session 2刚刚插入的数据。图中序号标识操作的顺序。
以下三种情况可以用来作为对比,图略:
若session 1使用read local表级锁,session 2执行update操作,则会被阻塞。
若session 1只使用read表级锁,自然地,session 2插入数据时会阻塞,直到读锁释放。
若session 1获得读锁(read和read local),则它再进行插入或更新操作时都会报错。
II、此时concurrent_insert变量为1。删除id=4的记录,表出现了空洞。如图,在session 1中对表使用read local表级锁,则session 2的插入操作会阻塞。
此时可以使用optimize table来进行碎片整理。如图,使用该命令之后,上述的并发插入操作不再阻塞。
III、还是上面的表(使用optimize table之前的有空洞的表),但设置concurrent_insert为2。如图,session 1对表使用了read local表级锁,session 2可以执行插入操作。
总结:
read local和read都是表级读锁,但前者允许在一定条件下,对读锁定的表执行并发插入操作。
concurrent_insert全局变量可以通过两种方式取得:在mysql命令行中执行show global variables like ‘%concurrent_insert%‘;,或者在shell命令行中执行mysqladmin -u username -p passwd variables |grep concurrent_insert;另外,可以通过set global concurrent_insert = 0/1/2设置该变量。它主要用于控制并发插入行为:
当它为0时,不允许并发插入(与实际测试不符?);
当它为1(默认值)时,若表不存在空洞,则允许并发插入;否则,不允许并发插入;
当它为2时,即使表存在空洞,也允许并发插入。
为什么表存在空洞与否对并发插入有影响呢?参考mysql命令行的help optimize table命令(“删除的行会在一个链表中维护,并且后续的插入操作会重用这些旧有的行的位置”),再结合上面的讨论,可知大概的组织图如下。
表中不存在空洞时,插入数据会在图中最下方的区域,此时无论concurrent_insert为1还是2,插入操作都不会和锁构成“竞争”关系,因此不会阻塞;表存在空洞时,由于系统要重用空洞区域,所以区分了以下情形:concurrent_insert为1时,认为是要重用空洞区域,但由于被锁定,所以插入操作阻塞;concurrent_insert为2时,认为先暂时不用空洞区域,而是插入到图中最下方的区域,和锁构不成“竞争”关系,因而插入操作不会阻塞。
optimize table碎片整理之后,图中空洞区域被重用,即不再存在空洞,因此concurrent_insert为1时,也可以执行并发插入操作了。
另外,update操作由于是更新read local锁定的区域内的记录,和锁构成“竞争”关系,所以会阻塞。
参考资料:
《高性能MySQL》
不断学习中。。。
标签:
原文地址:http://www.cnblogs.com/hanerfan/p/4234755.html