来源于公总号:58沈剑
事务的ACID特性,其中I表示隔离性(Isolation)
什么是事务的隔离性?
事务的隔离性是指,多个事务并发访问同一个数据库,各事务之间不会相互影响。也就是说多个事务之间要保持隔离。
一个事务为什么会干扰到其他事务呢?
我们举个例子,假如有InnoDB表:
t(id PK , name)
表中有三条记录:
1,shenjian
2,zhangsan
3,lisi
case 1
事务A先执行,处于未提交状态
insert into t values (4, wangwu)
事务B后执行,也未提交
select * from t;
如果事务B能够读取到事务A未提交的(4,wangwu)这条记录,事务A就对事务B产生了影响,这个影响就是“读脏”,读到了未提交的事务的操作记录。
case 2
事务A先执行:
select * from t where id = 1;
结果集为(1,shenjian)
这个时候,事务B 更新并提交:
update t set name = xxoo where id = 1;
commit;
事务A,再次执行相同的查询:
select * from t where id = 1;
结果集为:1,xxoo
这就是已提交的事务B对事务A产生的影响。这个影响是“不可重复读”,一个事务内相同的查询,得到了不同的结果
case 3
事务A先执行
select * from t where id > 3;
结果集为null
事务B后执行,并提交:
insert into t values (4,wangwu);
事务A首次查询id>3的结果为null,于是想插入一条id为4的记录
insert into t values (4,xxoo);
结果为 duplicate key!
对事务A而言,根据读取出来的数据,进行下一步的操作,但是已提交的事务B对事务A产生的影响,这个影响叫”幻读”。
总结一下,可以看到:
- 读脏:未提交的事务对另一个事务的影响
- 不可重复读:已提交的事务对另一个事务的影响
- 幻读:已提交的一个事务对另一个事务的影响
InnoDB 实现了哪几种事务的隔离级别
主要是实现了四种事务隔离级别,事务隔离级别主要对并发性和一致性的一个权衡和折中
- 串行化 Serializable
- 可重复读 Reapeted Read
- 读提交 Read Committed
- 读未提交 Read Uncommitted
InnoDB的四种隔离级别,分别是怎么实现的?
InnoDB 采用不同的锁策略(Lock Strategy)来实现不同的隔离级别
一、读未提交
并发性最好,一致性最差
这种情况下对select 语句不加锁。此种情况下,可能会产生“读脏”
二、串行化
并发性最差、一致性最好
这种情况下,对所有的select 语句都会转换成 select ... in share mode
如果有未提交的事务正在修改某一行,所有读取这行行的select 都会堵塞
以上两种在实际的生产环境中应用较少。不推荐使用
三、可重复读
RR :这是InnoDB 的默认隔离级别
在这种情况下:
(1)普通的select 使用快照读,这是一种不加锁的一致性读
(2)加锁的select ,update ,delete 等语句,它们的锁,依赖于它们是否在唯一索引上使用了唯一的查询条件或者范围查询条件
在唯一索引上使用唯一的查询条件,会使用记录锁,而不会封锁记录之间的间隔,即不会使用间隙锁与临键锁
范围查询条件,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复的读。
关于记录锁,间隙锁,临键锁的更多说明,详见《InnoDB,select为什么会阻塞insert?》
四、读提交
RC :在一般互联网公司应用比较广泛
这这种情况下:
(1)普通读是快照读;
(2)加锁的select,update,delete等语句,除了在外键约束检查及重复键检查时会封锁区间,其他时刻都只使用记录锁;
此时,其他事务的插入操作依然可以执行,就可能导致,读取到幻影记录。
总结
并发事务之间相互干扰,可能导致事务出现读脏,不可重复度,幻读等问题
InnoDB实现了SQL92标准中的四种隔离级别
(1)读未提交:select不加锁,可能出现读脏;
(2)读提交(RC):普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读;
(3)可重复读(RR):普通select快照读,锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录;
(4)串行化:select隐式转化为select ... in share mode,会被update与delete互斥;
InnoDB默认的隔离级别是RR,用得最多的隔离级别是RC