0%

数据库锁机制与事务

结合之前了解到的事务隔离机制,本文主要讲述加锁的细节:

  1. 锁的范围

  2. 加锁的时机

基础知识

锁的类型

共享锁(读锁、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。

加锁实例

  1. RR级别,条件列是精确,则快照读
  2. RR级别,条件列是精确非唯一索引且加锁,则当前读,聚簇索引上加S锁,非聚簇索引上根据前面锁的类型加锁;并加上gap 锁。
  3. RR级别,条件列是精确唯一索引且加锁,则当前读,聚簇索引上根据前面锁的类型加锁。
  4. RR级别,非精确索引(>,<)加锁,不管条件列是唯一索引还是非唯一索引都要gap锁,非唯一索引还需要对聚簇索引上S锁。

参考

【原创】惊!史上最全的select加锁分析(Mysql)