一、锁
1、锁的概述
InnoDB存储引擎中,事务的隔离性主要是由锁机制实现的。开发多用户的应用,很大的一个难点就在于并发访问:一方面既要最大程度地实现数据库的并发访问,另一方面又要确保每个用户能以一致的方式读取、修改数据,为此,我们需要锁机制。
锁机制是数据库系统区别于文件系统的另一个关键特性,锁机制用于管理对共享资源的并发访问,保证数据的完整性和一致性。这里的共享资源不仅仅是行数据,还包括缓冲池中的LRU列表,删除、添加、移动LRU列表中的元素时,有需要锁来保证一致性。
2、锁的类型
(1)共享/排他锁
MySQL提供了两种锁的粒度:行级锁、表级锁。一般而言,锁的粒度越小,并发度越高;锁用得越多,开销越大(锁的各种操作如获取锁、释放锁、检查锁状态等都会增加系统开销)。因此我们通常只锁定尽可能少的数据量,另外我们需要在锁开销和并发度之间找好平衡点。
InnoDB存储引擎提供两种行级锁:
共享锁(S Lock):允许事务读一行数据;
排他锁(X Lock):允许事务删除或更新一行数据。
如果事务T1已经获得行r的共享锁,那么事务T2可以立即获得行r的共享锁以读取数据(锁兼容);如果事务T1已经获得行r的共享锁/排他锁,那么事务T2要想获得行r的排他锁/共享锁或排他锁必须等待事务T1释放锁(锁不兼容)。共享锁和排他锁均用在事务中,随着事务的结束而释放。
(2)意向锁
InnoDB存储引擎支持意向锁的锁方式以实现多粒度锁定功能,所谓的多粒度锁定就是允许事务在行级上的锁和表级上的锁同时存在。意向锁在原来的 X/S 锁之上引入了两种表锁 IX/IS 锁:
意向共享锁(IS Lock),事务有意向获得一张表中某几行的共享锁;
意向排他锁(IX Lock),事务有意向获得一张表中某几行的排他锁
意向锁机制的工作方式:意向锁将锁定的对象分为多个层次(InnoDB存储引擎分为表级、行级两个层次),要想对最下层的对象(最细粒度的对象,行)上锁,需要先对上层对象上锁(粗粒度的对象,表)。也就是说,1)事务要想获得某些行的S锁,必须先获得表的IS锁; 2)事务要想获得某些行的X锁,必须先获得表的IX锁。 而获得各种锁的兼容性如下图所示:
从上图可以看出:意向锁之间始终是兼容的,表级的共享/排他锁之间的兼容性则跟前面行级的共享/排他锁之间的兼容性一样,至于意向锁与表级的共享/排他锁之间则是我们关注的重点,这也是意向锁机制存在的意义。我们可以看到,排他锁是很强的锁,不与其他类型的锁兼容。而修改和删除某一行的时候,必须获得强锁,禁止这一行的其他并发,以保障数据的一致性。
不过问题来了,按理说即时没有意向锁机制,只存在行级锁和表锁,那么在并发修改和删除时,也是可以保证数据的一致性的。比如事务T要给表A加X锁,它会判断此时是否存在其他事务已经获得表A的表锁 或者 已经获得表A的任意一行的锁,如果存在,则事务T阻塞直到可以获得锁。既然如此为什么还要有意向锁呢?
原因在于上面的过程中,当检测是否有其他事务对表A的任意一行已经加了锁的过程中,需要对表A的每一行都检测一次,而这种遍历是非常耗时间的。而在意向锁机制下的话,其他事务要想获得表A任意一行的行级锁,必须先获得表A的意向锁,这样一来,当事务T想给表A加X锁时,就可以马上检测到其他事务已经给表A加上表级锁(X / IX / S / IS锁),X锁与其不兼容,所以事务T被阻塞。因此,意向锁机制是可以提高数据库的并发度的,这大概是InnoDB存储引擎使用意向锁的原因之一吧。
意向锁的实际例子:
select ... lock in share mode,由于要获得S锁,因此此SQL语句会先获得IS锁;
select ... for update, 由于要获得X锁,因此此SQL语句会先获得IX锁
二、四种并发访问问题
三、四种事务隔离级别
四、总结
(1)InnoDB使用共享锁,提高读读并发;使用互斥锁,保证同一行记录修改与删除的串行性,从而保证数据强一致;
(2)InnoDB使用MVCC,提高写读并发
(3)InnoDB使用插入意向锁,提高插入并发
参考资料:
1. 《MySQL技术内幕 InnoDB存储引擎》
2. https://www.itcodemonkey.com/article/8000.html
3. https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks
4. 《高性能MySQL》
来源:CSDN
作者:JeremyChan1887
链接:https://blog.csdn.net/sinat_30973431/article/details/103683071