事务
隔离级别[1]
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
读未提交 Read uncommitted | ○ | ○ | ○ | ○ |
读已提交 Read committed | × | ○ | ○ | ○ |
可重复读 Repeatable read | × | × | ○ | × |
串行化 Serializable | × | × | × | ○ |
读未提交 Read uncommitted
事务中的修改即使没有提交,对其他事务也是可见的
事务可以读取到未提交的数据,被称为脏读
性能并不比其他级别好很多,但是问题多多,一般不用
读已提交 Read committed
事务开始时,只能看见已经提交了的事务所做的修改
事务从开始到直到提交之前,其所做的修改对其他事务来说是不可见的
问题:可能会产生不可重复读的问题,因为两次执行同样的查询,可能得到不同的结果
场景:事务A执行过程中先后查询了两次,事务B在事务A的两次查询之间修改了符合事务A的查询条件的记录,导致事务A中的两次查询结果不同
可重复读 Repeatable read(MySQL的默认事务隔离级别)
可重复读解读了脏读和不可重复读的问题,保证了同一个事务中多次读取同样记录的结果一致,但是不能解决幻读(Phantom Read)的问题
幻读:当事务A在读取某个范围内的记录时,另一个事务又在这个范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行
场景:事务A先查询了数据库中最大的ID为9,于是给自己将要插入的数据的ID设为10,在事务A进行过程中,事务B先插入了一个ID为10的记录,事务A在插入过程中报错,因为INSERT也是一次隐式的读取
InnoDB和XtraDB通过MVCC解决了读数据情况下的幻读问题,对于修改和新增依然存在幻读,于是又发展了next-key locking来处理修改和新增场景下的幻读,当前的MySQL应该是没有幻读问题的
串行化 Serializable
- Serializable
- 最高隔离级别,所有事务串行执行
- Serializable
- 会在所读取的每一行数据上都加锁
可能导致大量的超时和锁争用的情况,需要保证强一致性和可以接受没有并发的情况下使用
MVCC
MVCC只在Repeatable read和Read committed两个隔离级别下工作
Read uncommitted永远只读最新版本,而不是符合当前事务版本的数据行,Serializable会对所有的读数据加锁
版本号[2]
- 系统版本号:一个递增的数字,每开始一个新的事务,系统版本号就会自动递增
- 事务版本号:事务开始时的系统版本号
SELECT
select时读取数据的规则为:
- 创建版本号<=当前事务版本号
- 删除版本号为空或>当前事务版本号
创建版本号<=当前事务版本号保证取出的数据不会有后启动的事务中创建的数据
删除版本号为空或>当前事务版本号保证了至少在该事务开启之前数据没有被删除,是应该被查出来的数据。
INSERT
为新插入的每一行保存当前系统版本号作为行版本号
UPDATE
插入一条新纪录,保存当前事务版本号为行创建版本号
同时保存当前系统版本号到原来的行作为行删除版本号
DELETE
为删除的每一行保存当前系统版本号作为行删除版本号
参考文献
[1] 高性能MySQL 第三版 P8 事务隔离级别 /P12 MVCC 豆瓣读书
[2] MySQL的可重复读级别能解决幻读吗 掘金