本文内容:
- 锁
- MVCC
- 隔离级别的实现
锁
概述:用来维护事务的一致性和隔离性,在高并发场景下,如果加锁过度,会极大的降低并发处理能力
分类:
- 行锁:
- 共享锁S:允许持有锁的事务读取一行
- 互斥锁X::允许持有锁的事务更新、删除一行
- (SX互斥,SS共享)
- 记录锁:锁一个索引记录,表中没有索引时,会自动为主键创建一个聚簇索引
- 间隙锁:锁两个索引记录的间隙 或 第一个索引记录的前面 或 最后一个索引记录的后面的间隙
- 比如 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 这里用的就是间隙锁
- Gap锁的唯一目的是防止其他事务插入到Gap中
- 间隙锁可以共存
- 一个事务获取的间隙锁并不阻止另一个事务在同一间隙上获取间隙锁
- 共享和独占间隙锁之间没有区别, 它们彼此不冲突,并且执行相同的功能
- Next-Key:记录锁+间隙锁,锁索引记录 以及 它前面的间隙
- 如果索引记录包含10, 11, 13, and 20值,则可能的Next-Key锁范围:(negative infinity, 10] (10, 11] (11, 13] (13, 20] (20, positive infinity)
- 表锁:
- 意向锁:实现多粒度锁 -- 允许表锁、行锁共存,表明事务打算对表中的记录使用哪种锁(S、X),除了全表请求外不锁任何东西
- 意向共享锁IS:事务打算对表中各行加共享锁
- 意向互斥锁IX:事务打算对表中各行加互斥锁
- 意向锁协议如下:
- 事务想对行记录加共享锁S前,必须先取得意向共享锁IS 或 约束力更强的意向互斥锁IX
- 事务想对行记录加互斥锁S前,必须先取得意向互斥锁IX
- 表锁和行锁兼容性:
- Insert Intention Locks:
- 在插入行之前由插入操作设置的一种间隙锁,此锁表示插入的意图,
- 如果插入到同一索引间隙中的多个事务,不在间隙中的同一位置插入,则它们无需等待对方
- AUTO-INC:特殊的表级锁,插入到具有自增列的表的事务使用
- 其他事务必须等待自己向该表中插入值,以便插入行接收连续的主键值
MVCC
概述:多版本并发控制,空间换时间,通过保存数据每次修改的版本来实现读不加锁,提高并发性(读写并发)
实现:事务号+回滚日志(版本链)+READ VIEW
- 每行数据后有两个隐藏列(修改事务号、回滚指针),
- 事务号:事务开启时得到一个递增事务号
- 回滚指针:将同一行数据的不同版本串行化得到版本链
- 事务修改数据时会加互斥锁,然后将修改前版本放到undo log里,通过回滚指针与新数据相连
- READ VIEW:
- 存储了目前活跃(还在执行)的事务号trx_ids
- 最小活跃事务号low_trx_id,最大活跃事务号up_trx_id
- 当前事务号
- 查询数据时通过回滚指针在数据版本链上找第一个对该事务可见的数据
- 可见性判定:
- 若数据版本号<low_trx_id,表明数据是在当前事务开启前就已经提交的事务修改的,可见
- 若数据版本号>up_trx_id,表明数据是在当前事务开启后才开启的事务提交修改的,不可见
- 其他情况,则依据隔离级别
- RC级别下,可见,不可重复读,每次读最新修改的
- RR级别下,不可见,可重复读,读第一次读到的
MVCC简化版 -- 高性能MySQL
系统有版本号,每开启一个事务,系统版本号+1,事务版本号为启动时的系统版本号
在每行数据后添加两个额外的隐藏的值来实现MVCC(一个是创建版本号、一个是删除版本号)
SELECT:行记录的创建版本号<=本事务的版本号 and 删除版本号>本事务的版本号
INSERT:增加一个记录,创建版本号=当前事务版本号
DELETE:删除版本号=当前事务版本号
UPDATE:
增加一行记录,创建版本号=当前事务版本号,
旧记录的删除版本号=当前事务版本号
隔离级别与锁
Read UnCommitted:无锁
Read Committed:MVCC读(每次读最新版本快照),写加锁
Repeatable Read:
- 读(快照读):MVCC(第一次读最新版本,之后读第一次读的版本)
- 写(当前读):
- 记录锁:修改
- GAP锁防止别的事务新增
- Next-Key:行锁防止别的事务修改或增加
Serializable(悲观锁):读+共享锁,写+排他锁,读写互斥
注:不可重复读和幻读的区别:不可重复读重点在于update,幻读在于insert
一致性锁定读:
- select ... for update 互斥锁,这情况也可以一致性非锁定读(MVCC)
- select ... lock in shared mode 共享锁
怎么保证数据方面的并发问题---从数据库隔离级别去介绍
权衡并发度--性能--存储,两阶段(加锁先于解锁)
- RR:性能最高,存在各种问题
- RC:加了写锁,事务提交才释放,写多的场景就会影响性能;
- 所以用MVCC提高读性能,保存不同版本的数据
- RR:MVCC,Next-Key
学习自
- MySQL中MVCC的正确打开方式(源码佐证)
- MySQL官方文档_innodb-multi-versioning
- MySQL InnoDB MVCC实现
- mysql版本链&readview
- Innodb中的事务隔离级别和锁的关系(美团技术团队)