MySQL 锁机制

乐观锁

程序实现

  • 版本号
  • 时间戳

悲观锁

全局锁

  • Flush tables with read lock (FTWRL)

表级锁

表锁(MySQL layer)

  • 加锁:lock tables … read/write
  • 解锁:unlock tables

当线程A lock tables t1 read,线程A和其他线程都只能读 t1,不能写t1
当线程A lock tables t1 write,线程A可以读写t1,其他线程不能读写t1

元数据锁(MySQL layer)

元数据锁MDL(metadata lock),元数据:指表结构

  • CRUD(DML):加 MDL 读锁
  • 表做结构变更(DDL):加 MDL 写锁

兼容性
读锁之间不互斥;读锁和写锁,写锁和写锁间互斥

意向锁(InnoDB)

共享读锁(IS)
  • 在给行记录加S锁时,会先自动给数据所在表加IS锁
排他写锁(IX)
  • 在给行记录加X锁时,会先自动给数据所在表加IX锁

意向锁的作用
事务A锁住了表中的一行,让这一行只能读(数据库自动给该表增加意向共享锁),不能写。
事务B申请整个表的写锁。

  1. 判断表是否已被其他事务用表锁锁表
  2. 发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

如果没有意向锁则是这样的:

  1. 判断表是否已被其他事务用表锁锁表
  2. 判断表中的每一行是否已被行锁锁住。

如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突。

如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了,有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果。

兼容性

是否兼容(表级)ISIXSX
IS
IX
S
X
  1. 意向锁相互兼容,因为IX、IS只是表明申请更低层次级别元素(比如 page、记录)的X、S操作。
  2. 表级S锁和X、IX锁不兼容:因为上了表级S锁后,不允许其他事务再加X锁。
  3. 表级X锁和 IS、IX、S、X不兼容:因为上了表级X锁后,会修改数据,所以即使是行级排他锁,因为表级锁定的行肯定包括行级锁定的行,所以表级X和IX、X都不兼容。
  4. 上了行级X锁后,行级X锁不会因为有别的事务上了IX而堵塞,MySQL是允许多个行级X锁同时存在的,只要他们不是针对相同的数据行。

行级锁(InnoDB)

重点:InnoDB行级锁基于索引实现

两阶段锁协议

  • 定义:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。
  • 可重复读隔离级别下遵守两阶段提交,事务结束才释放锁。read-commited 语句执行完就释放“不满足条件的行”的行锁,而不是在事务结束的时候才释放。

普通select语句默认不会加任何锁类型,是一致性读(MVCC),所以加锁不会阻塞普通select

共享读锁(S)

  • 手动加锁:select … lock in share mode(当前读)
    使用索引时,为行锁;
    未使用索引时,行锁会升级为表锁。

排他写锁(X)

  • 自动加锁:select for update (当前读)
  • 自动加锁:DML(insert、update、delete)

兼容性

  • 读锁写锁间互斥,写锁写锁间互斥
  • 读锁读锁间兼容

行锁的具体实现:

Record Locks

记录锁,专门对索引项加锁

  1. 记录锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。
  2. record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。

所以说当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加X锁,类似于表锁。

Gap Locks

间隙锁,对索引项之间间隙加锁

  1. 区间锁, 仅仅锁住一个索引区间,开区间:()。
  2. 在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。比如在 1、2、3中,间隙锁的可能值有 (∞, 1),(1, 2),(2, ∞),
  3. 间隙锁可用于防止幻读,保证索引间的不会被插入数据
  4. 间隙锁在可重复读隔离级别下才会生效的。如果把隔离级别设置为读提交的话,就没有间隙锁了。
  5. 间隙锁的引入,可能会导致同样的语句锁住更大的范围,影响了并发度,如果不需要可重复读的隔离级别,可以使用读提交隔离级别 + binlog_format=row

兼容性

  • 跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作。间隙锁之间都不存在冲突关系。
Next-Key Locks

临键锁,是前面两种的组合,对索引项以及之间的间隙加锁

  1. record lock + gap lock, 左开右闭区间:(]。
  2. 默认情况下,innodb使用next-key locks来锁定记录。
  3. Next-Key Lock 的加锁过程是拆分为:加间隙锁和加行锁两段来执行的
  4. 但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。
  5. Next-Key Lock在不同的场景中会降级:
场景降级成的锁类型
使用唯一索引,精确匹配(=),且记录存在Record Lock
使用唯一索引,精确匹配(=),且记录不存在Gap Lock
使用唯一索引范围匹配(< 和 >)Record Lock + Gap Lock

幻读相关

  • 幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
  • 在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。
  • Gap 锁可以避免出现幻读

加锁规则

  1. 加锁的基本单位是 next-key lock。next-key lock 是前开后闭区间。
  2. 查找过程中访问到的对象才会加锁。
  3. 索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
  4. 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
  5. 唯一索引上的范围查询会访问到不满足条件的第一个值为止。
    (可能是一个BUG,据说8.0.18以后版本已修复)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值