1.锁的一些基础
锁是为了保护并发场景中临界资源,保证其有序变更。锁的粒度越粗,锁住的范围越大,并发度越低。
2.本文中主要探讨常用的一些锁:行锁、间隙锁、next-key lock、表级锁、MDL。
3.行锁
分为两类,共享锁S,排他锁X,对同一行数据而言,S可以兼容S,X不能兼容其他锁。
意向锁:对小粒度数据加锁前,一般对大粒度数据加意向锁。例如,对一行记录加S,对该行记录所在的表加IS。意向锁和行锁之间的兼容性很容易推理。
为什么要有意向锁?一个表中已经有了行锁的时候,此时想要加另一个行锁,先判断和意向锁是否冲突,如果冲突,就不必对每一行的行锁进行扫描了。
4.两种常见的涉及一致性以及锁的写法
select ... for update:对读取的该行加X锁。在rr级别下,被锁住的行也是可以读的。
select ... for share mode:对读取的该行加S锁。
5.主键自增与锁
AUTO-INC locking,不等待事务完成,完成对自增长id插入的sql语句后就释放,增加并发度。后续升级用innodb_autoinc_lock_mode,如果存在批量插入情况,一次性插入的值可以确定,就可以直接一次性申请多个id,合并申请释放一次。
6.行锁、间隙锁、next-key lock
间隙锁是为了解决幻读的问题。
next锁形式是多个区间,左开右闭。
法则:
等值查询,唯一索引,next锁降级为行锁,唯一索引必须保证唯一,每次插入要检查,不一样一定能插入新行,没必要锁住行。
等值查询,最后扫描的一个行不满足条件的时间,最后一个退化为间隙锁(右开)。
访问到的对象才会加锁。走索引和全表扫描是两情况,覆盖索引不访问主键索引树,主键索引依然可以操作。
7.MDL
MDL是自动加上的,不需要显式使用,增删改查DML是读锁,表结构变更DDL是写锁。
经典死锁问题:线程A申请了DML,线程B申请DDL被阻塞,会阻塞后面所有的DML,如果查询频繁或者有重试机制,线程会爆满。
可以为DDL操作设置心跳机制。