锁
作为一名程序员我相信对于锁这个概念已经很熟悉了,我们都知道锁的种类一般分为乐观锁和悲观锁两种,两者都是用来解决并发问题的方法或者思想,InnoDB 存储引擎中使用的就是悲观锁,而按照锁的粒度划分,也可以分成行锁和表锁。
乐观锁
乐观锁是一种基于冲突检验的思想设计的,举个例子,当线程要对数据进行修改时 ,首先会去判断数据是否被修改过,如果没有被修改那么修改这条数据,否则放弃这次修改或者对修改进行重试.在整个的执行过程中其实都没有对数据库进行加锁;
悲观锁
悲观锁的思想是不管对资源的操作是否发生冲突,在进行操作之前都会对资源进行加锁,确保同一时刻只有有限的线程能够访问该资源,其他想要尝试获取资源的操作都会进入等待状态,直到该线程完成了对资源的操作并且释放了锁后,其他线程才能重新操作资源;
乐观锁和悲观锁的使用
乐观锁不会存在死锁的问题,但是由于需要先验证再更新,所以当冲突频率和重试成本较高时更推荐使用悲观锁,而需要非常高的响应速度并且并发量非常大的时候使用乐观锁就能较好的解决问题,在这时使用悲观锁就可能出现严重的性能问题;在选择并发控制机制时,需要综合考虑上面的四个方面(冲突频率、重试成本、响应速度和并发量)进行选择。
innodb锁的种类
我们都知道对于数据的操作无非分为两种读和写,因此数据库在实现锁时对这两种操作使用不同的锁;
共享锁(读锁):允许事务对一条行数据进行读取;
互斥锁(写锁):允许事务对一条行数据进行删除或更新;
在innodb中读是默认共享的,写是默认排他的.
锁的粒度
innodb支持多种粒度的锁,即支持行锁和表锁.
Innodb 实现了标准的行级锁,也就是支持共享锁(Shared Lock)和互斥锁(Exclusive Lock).
行共享锁:当innodb搜索表索引时,它会在遇到的索引记录上设置共享锁。
行排他锁:当innodb扫描表索引时,它会在遇到的索引记录上设置排他锁。
innodb为了支持多粒度锁定,InnoDB 存储引擎引入了意向锁(Intention Lock),意向锁是一种表级锁。
与行锁提到的两种锁的种类相似的是,意向锁也分为两种:
意向共享锁:事务想要在获得表中某些记录的共享锁,需要在表上先加意向共享锁;
意向互斥锁:事务想要在获得表中某些记录的互斥锁,需要在表上先加意向互斥锁;
随着意向锁的加入,锁类型之间的兼容矩阵也变得愈加复杂:
意向锁的作用:意向锁其实不会阻塞全表扫描之外的任何请求,它们的主要目的是为了表示是否有人请求锁定表中的某一行数据。
如果看到这里你有疑问不妨想一想这种情景:
如果没有意向锁,当已经有人使用行锁对表中的某一行进行修改时,如果另外一个请求要对全表进行修改,那么就需要对所有的行是否被锁定进行扫描,在这种情况下,效率是非常低的;不过,在引入意向锁之后,当有人使用行锁对表中的某一行进行修改之前,会先为表添加意向互斥锁(IX),再为行记录添加互斥锁(X),在这时如果有人尝试对全表进行修改就不需要判断表中的每一行数据是否被加锁了,只需要通过等待意向互斥锁被释放就可以了。
锁的实现
到目前为止已经对 InnoDB 中锁的概念有一定的了解,也清楚了在对数据库进行读写时会获取不同的锁,接下来我将介绍锁是如何添加到对应的数据行上的,我们会分别介绍几种锁的算法:Record Lock、Gap Lock 和 Next-Key Lock等。官方介绍
Record Lock(记录锁).
记录锁(Record Lock)是加到索引记录上的锁,假设我们存在下面的一张表 users:
CREATE TABLE users(id INT NOT NULL AUTO_INCREMENT , last_name VARCHAR(255) NOT NULL , first_name VARCHAR(255) , age INT, PRIMARYKEY(id) , KEY(last_name),KEY(age))engine=innodb;
当我们使用 索引键(id或者age) 作为 SQL 中 WHERE 语句的过滤条件时,innodb 会通过索引建立的 B+ 树找到行记录并添加记录锁,这里需要注意的是如果使用 非索引键(name) 作为过滤条件时,由于 innodb 不知道待操作的记录具体存放的位置,也无法对将要操作哪条记录提前做出判断就会锁定整个表。
Gap Lock(间隙锁)
间隙锁(Gap Lock)是对索引记录中的一段连续区域的锁;当使用类似 SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;的 SQL 语句时,就会阻止其他事务向表中插入 id = 15 的记录,因为整个范围都被间隙锁锁定了。
间隙锁是存储引擎对于性能和并发做出的权衡,并且只用于某些事务隔离级别。
虽然间隙锁中也分为共享锁和互斥锁,不过它们之间并不是互斥的,也就是不同的事务可以同时持有一段相同范围的共享锁和互斥锁,它唯一阻止的就是其他事务向这个范围中添加新的记录。
Next-Key Lock
Next-Key Lock是一种特殊的锁,它会在索引记录上加记录锁并且在索引记录之前加间隙锁.
假设索引包含值10,11,13和20.此索引的可能的 Next-Key Lock包括以下间隔,其中圆括号表示排除间隔端点,方括号表示包含端点:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
默认情况下,InnoDB以 REPEATABLE READ事务隔离级别运行。在这种情况下,InnoDB使用下一键锁进行搜索和索引扫描,这会阻止幻影行(请参见第14.7.4节“幻影行”)。
Insert Intention Locks(插入意向锁)
Insert Intention Locks(插入意向锁)是这样设计的:针对insert操作,假设插入前,该间隙已经被间隙锁锁定,那么Insert会申请插入意向锁。一个间隙锁中可以存在多个插入意向锁,如果多个事务插入到相同的索引间隙中,如果它们不在间隙中的相同位置插入,则无需等待其他事务。假设存在值为4和7的索引记录。有两个插入值5和6的单独事务,在获取插入行上的排他锁之前,每个事物都使用插入意图锁锁定4和7之间的间隙,但是不要互相阻塞,因为操作是不冲突的.插入意向锁的设计是为了提升插入效率的。
AUTO-INC锁
AUTO-INC锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁.
在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待对该表执行自己的插入,以便第一个事务插入的行接收连续的主键值。
Predicate Locks for Spatial Indexes(谓词锁)
InnoDB支持SPATIAL 对包含空间列的列进行索引(请参见 第11.5.8节“优化空间分析”)。
要处理涉及SPATIAL索引的操作的锁定 ,下一键锁定不能很好地支持REPEATABLE READ或 SERIALIZABLE事务隔离级别。多维数据中没有绝对排序概念,因此不清楚哪个是 “ 下一个”密钥。
要为具有SPATIAL索引的表启用隔离级别 ,请InnoDB 使用谓词锁。
声明:文章图片以及相关内容借鉴自https://draveness.me/mysql-innodb.html
如有侵犯您的知识产权和版权问题,请通知本人,本人会即时做出处理删除文章。
如果本博客的文章在知识点上有错误,欢迎指出错误所在,欢迎多多交流。谢谢!
网友评论