结合之前了解到的事务隔离机制,本文主要讲述加锁的细节:
基础知识
锁的类型
共享锁(读锁、S锁)
上了该锁,并不影响其它事务的读取,而无法修改。
排他锁(写锁、X锁)
某一事务对某行数据上了排他锁,其它事务无法读写该行数据。
意向共享锁(IS锁)
一个事务在获取(任何一行/或者全表)S锁之前,一定会先在所在的表上加IS锁。
意向排他锁(IX锁)
一个事务在获取(任何一行/或者全表)X锁之前,一定会先在所在的表上加IX锁。
Q:意向锁有什么用呢?
[InnoDB 的意向锁有什么作用?](InnoDB 的意向锁有什么作用? - 发条地精的回答 - 知乎
https://www.zhihu.com/question/51513268/answer/127777478)
想象这样的一个场景:事务A给某行加上了读锁,那么其它事务只可以读,无法写;事务B需要向数据库申请一个整个表的写锁,此时数据库需要判断表里是否存在已经被加锁的数据行,来决定此时事务B加锁的成功与否,拒绝加锁的话,需要阻塞事务B。但是其中有一个问题:数据库需要遍历判断每一行是否没有被锁,效率不高。
意向锁就是解决这个问题,数据去判断下是否存在意向锁,有意向共享锁则说明行级锁的存在或者即将有行级锁的存在,因而无需遍历整个表。
注意的是:IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
锁的范围
Record Locks:行锁,是对索引进行加锁,而非对具体数据行加锁。
Gap Locks:间隙锁,对索引的间隙加锁,但不包括记录本身,防止其他事务插入数据,只存在于RR和Serializable。
Next-Key Locks:锁定一个范围,并且锁定记录本身。相当于Record Locks+Gap Locks的组合
加锁条件下(for update/lock in share mode)补充:
如果条件没有走索引的话,会进行全表扫描,表锁
* 表锁其实就是利用了**Next-Key Locks**(Record Locks+Gap Locks)
若走了非唯一索引,则是Gap Lock或者Next-Key Lock。
* 如果写入的数据行,是表中不存在的,只能加Gap Locks,因为没有这行数据,无法加行锁,所以是个开区间。
- 如果数据行没有主键,数据则根据隐藏的Rowid(自增)排序。
若走了唯一索引,则是Record Locks。
上述说的是RR和Serializable中成立,若RU和RC,没有间隙锁,无论条件列
快照读和当前读
快照读
select * from table where id = ?;
读的是数据库记录的快照版本,是不加锁的。
- Seralizable是加锁的
当前读
select * from table where id = ? lock in share mode;
读加上共享锁(S锁)
select * from table where id = ? for update
读加上排他锁(X锁)
Innodb索引
主键索引是构建的一棵聚簇索引的B+树
• 聚簇索引的叶子节点为磁盘上的真实数据。非聚簇索引的叶子节点还是索引,指向聚簇索引B+树。
• innodb一定存在聚簇索引,因此行锁最终都会落到聚簇索引上!
• RR/Serializable+条件列是非聚簇索引
○ 非聚簇索引是非唯一索引的情况,他和唯一索引的区别就是通过索引进行精确查询以后,不仅存在record lock,还存在gap lock。
○ 通过唯一索引进行精确查询后,只存在record lock,不存在gap lock。
加锁实例
- RR级别,条件列是精确,则快照读
- RR级别,条件列是精确非唯一索引且加锁,则当前读,聚簇索引上加S锁,非聚簇索引上根据前面锁的类型加锁;并加上gap 锁。
- RR级别,条件列是精确唯一索引且加锁,则当前读,聚簇索引上根据前面锁的类型加锁。
- RR级别,非精确索引(>,<)加锁,不管条件列是唯一索引还是非唯一索引都要gap锁,非唯一索引还需要对聚簇索引上S锁。
参考