InnoDB事务隔离级别及其实现原理
Posted Hollis Chuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了InnoDB事务隔离级别及其实现原理相关的知识,希望对你有一定的参考价值。
“ 大部分服务端系统都是数据密集型应用,主要的功能是基于数据库对各种业务数据进行增删查改。在互联网这种高并发场景,如何确保数据的准确性以及保证系统的吞吐量,事务的隔离性有很大一部分功劳”
本文主要探究mysql数据库InnoD存储引擎的事务隔离级别及其背后实现原理,并且会回答以下问题:
不同事务隔离级别解决什么问题,如何解决的?
MVCC和数据库锁之间的相同和不同之处是什么?
如何根据自己系统的特性来选择事务隔离级别?
01
—
事务是什么?
事务是数据库一个不可分的工作单元,可以将多个操作步骤表示为一个步骤,事务拥有ACID四大特性即
原子性(Atomicity):事务的操作是不可分的,即使有多个操作步骤,要么全部执行成功,要么全部执行失败。背后的实现原理是通过undo log做到的,回滚的时候基于undo log上的数据,如果执行了INSERT,那就再做一次DELETE,如果做了一次UPDATE,则会做一个相反的UPDATE。
隔离性(Isolation):并行执行的事务之间互相不影响,即A事务操作但未提交的数据,不会影响B事务的处理结果。背后的实现原理是通过MVCC以及数据库锁做到的,这部分也是本文介绍的重点。
持久性(Durability):事务一旦提交,其结果就是持久的,即使发生宕机,事务提交的数据也能恢复。背后的实现原理是通过redo log做到的,每次提交事务redo log都会刷到磁盘里。redo log作为重做记录了数据库的物理日志,上面记录了某个数据页做了什么修改,即使宕机也可以根据redo log恢复数据页的内容。
一致性(Consistency):事务执行前后会从一个正确一致状态到另一个正确一致状态,一致性是ACID里约束最弱的特性,因为这个特性更像一个结果。即做到了原子性、隔离性、持久性,自然就做到了一致性。
当然上述关于事务ACID的定义都是理论上的,实际情况可能并没有严格满足,不同事务隔离级别表现并不相同,所以下面会讨论事务的隔离级别
02
—
事务异常情况
脏读:一个事务读到了另一个事务未提交的数据
时间 | 事务A | 事务B |
1 | 插入库存为10 | |
2 | 查询库存为10 | |
3 | 回滚事务 |
不可重复读:一个事务里多次查询一条数据,得到的结果不同
时间 | 事务A | 事务B |
1 | 查询库存为10 | |
2 | 修改库存为11 | |
3 | 提交事务 | |
4 | 查询库存为11 |
幻读:一个事务里多次查询一批数据,得到的结果不同,看到了新插入的行数据
时间 | 事务A | 事务B |
1 | 查询一个班级人数为10 | |
2 | 班级增加1人 | |
3 | 提交事务 | |
4 | 查询一个班级人数为11 |
03
—
基本概念
接下来会介绍隔离级别是如何解决这些异常情况的,先介绍几个基本概念
两阶段锁协议
在 InnoDB 事务中,行锁是在用到的的时候才加上的,但并不是不再用了就立刻释放,而是要等到事务提交或回滚时才释放
锁的类型
共享锁(shared lock):允许事务读一行数据
排他锁(exclusive lock):允许事务删除或更新一行数据
共享锁 | 排他锁 | |
共享锁 | 兼容 | 不兼容 |
排他锁 | 不兼容 | 不兼容 |
锁的算法
行锁(record lock):针对单行记录的锁
间隙锁(gap lock):锁定一个范围但不包含记录本身,在可重复读级别下才存在
Next-Key Lock:间隙锁+行锁,锁定一个范围并且包含记录本身,每个next-key lock都是前开后闭区间,假设数据库里有3条记录0,5,10 那么就有(-∞,0]、(0,5]、(5,10]、(10,+∞]4个可能的next-key lock,在可重复读、串行化级别下才存在
快照读与当前读
多版本并发控制(MVCC):一行数据有多个版本,根据事务Id获取一致性读视图,数据版相关内容维护在undo log里,在读已提交、可重复读级别下才存在,MVCC避免了锁的争抢,通过空间换时间来保证了数据读取的正确性
快照读:读取的是快照数据,可能并不是最新数据,如果MVCC生效就会走到MVCC背后相关的逻辑,最普通的查询语句就是快照读
select * from table
当前读:读取到的是最新数据,且读的时候会进行加锁
select * from table lock in share mode
读的时候会加一把共享锁
select * from table for update
读的时候会加一把排他锁
实际情况在获取共享锁、排他锁的过程,还有意向共享锁,意向排他锁的获取,对本文想阐述的内容关系不大,不做赘述。
04
—
事务隔离级别
SQL标准定义了四个隔离级别
读未提交(READ UNCOMMITED)
读已提交(READ COMMITED)
可重复读(REPEATABLE READ)
串行化(SERIALIZABLE)
脏读
不可重复读
幻读
读未提交
未解决
未解决
未解决
读已提交 已解决 快照读未解决,通过显示加锁可解决 未解决 可重复读
已解决
已解决
InnoDB引擎已解决
串行化
已解决
已解决
已解决
读未提交
特点:读的时候未对数据加锁,修改数据的时候对数据添加行级共享锁
效果:因为修改数据时候添加的是共享锁,所以会被其他事务读到,产生
脏读的问题
读已提交
特点:
(快照读)读的时候会通过MVCC的一致性视图读到最新的、已提交事务
的数据版本
(当前读)根据sql语句的不同读数据的时候,会加一把共享行锁(lock in
share mode)或者排他行锁(for update)
当读取数据不存在的时候,不会加锁。修改数据的时候会对数据行添加行
级排他锁,事务结束的时候才会释放,如果在执行语句的时候没有走到索
引,并且在修改的时候如果发现某行数据已经被锁定,这时会通过MVCC
读到最新的、已提交事务的数据版本再根据where条件来决定是否必须更
新改行,从而决定是否要等待锁的释放
效果:
(快照读)因为通过MVCC读取的是已提交事务的数据版本,所以不会存在
脏读。通过MVCC读到的数据,没有进行加锁操作, 所以其他事务仍然可
以对读到的数据进行修改并提交,这时候再次读取就发生了不可重复读。
(当前读)通过显示加锁,防止了其他事务对其行数据的修改,从而避免了
不可重复读
可重复读
特点:
(快照读)读的时候会通过MVCC的一致性视图读到开启事务时刻、已提
交事务的数据版本
(当前读)根据sql语句的不同读数据的时候,会加一把共享锁(lock in
share mode)或者排他锁(for update), 如果查询行不存在,next-key lock
则会退化成gap lock修改数据的时候会对数据行添加next-key lock不仅锁
住行数据本身,还锁住相邻的间隙,事务结束的时候才会释放
效果:
(快照读)因为通过MVCC读取的是开启事务时刻,已提交事务的数据版
本,所以无论其他事务怎么修改,看到的数据都是雷打不动的固定的数据
版本,这时候再次读取就避免了不可重复读。因为在修改数据的时候使用
了next-key lock,所以即使进行插入操作也会被锁阻塞,这样也避免了幻
读情况的产生。在实际情况中,锁的算法比较灵活 ,根据sql语句的不
同,和命中索引的区别,即有next-key lock+gap lock锁住更大范围的情
况,也有在唯一索引场景 next-key lock退化成 record lock以及gap lock
的情况,这里后面可以单独拉一篇文章进行阐述,内容较多
串行化
特点:
串行化和可重复读很相似,
区别是串行化级别会为每一个普通的
select *from table
语句隐式转化为
select *from table lock in share mode
给索引增加一个共享的next-key lcok,同时也不存在通过MVCC读取数据
的情况。针对修改,和可重复读一样会通过next-key lock进行索引数据行
的锁定
效果:
和不可重复读效果一样,解决了脏读、不可重复读、幻读,并且比不可重
复读的并发控制更加严格
05
—
如何选择?
MySQL InnoDb引擎默认的事务隔离级别是可重复读,但是阿里内部设置
的数据库隔离级别是读已提交,这是为什么呢?谈谈我自己的看法
不可重复读并不一定是缺点,虽然第二次读到的数据和第一次不一样,但是第二次的数据来自于其他事务提交过的正确数据,那么就代表第一次读的数据已经是旧的、过时的数据了,那为什么要基于一份旧的、过时的数据进行操作呢?而是及时止损,用最新的数据进行后续业务操作处理。
Oracle的默认事务隔离级别是读已提交,猜测是之前去IOE的过程中,方便Oracle到MySQL的迁移,沿用了读已提交
虽然可重复读用了大量锁算法来保证可重复读和幻读,但是幻读对业务影响有限,如第一点所述,幻读出来新增的数据也确实是其他事务正确提交得来的,幻读出来的数据也是最新的数据。当然幻读在出库备库数据复制过程可能带来数据不一致,但是可以通过修改bin log的数据格式得到解决
读已提交锁算法更简单,锁粒度更细并发度更高,更加适合互联网场景,同时方便开发人员排查死锁等线上问题
所以在没有对不可重复读、幻读有强烈诉求的情况下,读已提交是一个不错的选择
06
—
参考资料
InnoDB Locking and Transaction Model
《MySQL技术内幕 InnoDB存储引擎 第2版》
《数据库系统内幕》
本文转载自公众号:
以上是关于InnoDB事务隔离级别及其实现原理的主要内容,如果未能解决你的问题,请参考以下文章