MySQL 知识 -- 锁
本文最后更新于:10 分钟前
⚡MySQL 锁
MySQL中有着Lock和Latch的概念,在数据库中,这两者都可以被称为“锁”,但是两者有着截然不同的含义。
-
Latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差,在InnoDB引擎中,Latch又可以分为 mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
-
Lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。
-
锁种类
⚡InnoDB 锁
⚡行锁
-
共享锁(S): 允许事务读取一行数据
select * from tableName where … lock in share mode;
-
独占锁(X): 允许事务新增或者修改一行数据
select * from tableName where … for update;
S 锁和 S 锁之间是可以兼容的,X 锁不能和任何锁兼容
⚡表锁
为了实现多粒度的锁机制,InnoDB 还有两种内部使用的意向锁,由 InnoDB 自动添加,且都是表级别的锁。
-
意向共享锁(IS): 事务即将给表中的各个行设置共享锁,事务给数据行加 S 锁前必须获得该表的 IS 锁
IS 锁只是不能和 X 锁共存
-
意向排他锁(IX): 事务即将给表中的各个行设置排他锁,事务给数据行加 X 锁前必须获得该表 IX 锁
IX 锁不能和 X 和 S 锁共存
意向锁的主要目的是为了使得行锁和表锁共存
⚡InnoDB 存储引擎使用三种行锁的算法用来满足相关事务隔离级别的要求
InnoDB 存储引擎使用三种行锁的算法用来满足相关事务隔离级别的要求
-
Record Locks (就是行锁)
把索引记录上锁
如果表中没有定义索引,InnoDB 会默认为该表创建一个隐藏的聚簇索引,并使用该索引锁定记录
-
Gap Locks
该锁会锁定一个范围,但是不包括记录本身
GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况
-
Next-key Locks
该锁就是 Record Locks 和 Gap Locks 的组合:即锁定一个范围并且锁定该记录本身
也就是说,除了锁标识的范围,还会锁住后面的一个 Gap 范围
InnoDB 使用 Next-key Locks 解决 在 Repeatable read 下的幻读问题
例如:
CREATE TABLE `test` ( `id` int(11) primary key auto_increment, `xid` int, KEY `xid` (`xid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into test(xid) values (1), (3), (5), (8), (11);
注意在 xid 上加了索引
Session A 执行后会锁住的范围:
(5, 8], (8, 11]
除了锁住8所在的范围,还会锁住下一个范围,所谓Next-Key
这样,Session B 执行到第六步会阻塞,第七步也会阻塞,但是并不阻塞第八步,第九步也不阻塞
上面的结果似乎并不符合预期,因为11这个值看起来就是在 (8, 11] 区间里,而5这个值并不在(5, 8]这个区间里
辅助索引 (xid 上面的索引) 中黄色部分是被 record lock锁住的行,除此之外还有两个 Gap Lock,锁住了上面说的范围
先说为什么锁住了5的插入,观察主键索引,主键索引是自增的,因此在 id=4 这条记录之前,是不允许插入一条 xid=5 的记录的。
反之,也就是说,间隙锁虽然是说是左开右闭实际上全是开区间!
只不过,插入记录的主键大于行锁锁住的行主键就会等待锁,而小于行锁锁住的行主键就不会上锁!
所以,本质上是两个条件:一个是间隙锁范围+行锁记录,还有一个是行锁记录的主键 id 和另一个要插入或者更新的主键 id 值有关
不锁定xid=11的写入还是可以用 id 是自增的解释,B+ 树是有序的,并不会阻塞后续的插入
…
需要注意的是,如果索引有唯一属性,则 InnoDB 会自动将 Next-key Locks 降级为 Record Locks
-
死锁
InnoDB 引擎采取的是 wait-for graph 等待图的方法来自动检测死锁,如果发现死锁会自动回滚一个事务
本博客所有文章除特别声明外,均采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 。转载请注明出处!