MySQL事务隔离级别
ACID
- 原子性(Atomic): 事务中的多个操作,不可分割,要么都成功,要么都失败; All or Nothing.
- 一致性(Consistency): 事务操作之后, 数据库所处的状态和业务规则是一致的; 比如a,b账户相互转账之后,总金额不变;
- 隔离性(Isolation): 多个事务之间就像是串行执行一样,不相互影响;
- 持久性(Durability): 事务提交后被持久化到永久存储.
几个"读"概念
类型 | 定义 |
---|---|
脏读 | 可以读取其他事务未提交的数据 |
不可重复读 | 同一个事务中多次执行同一个select, 读取到的数据发生了改变(被其它事务update并且提交) |
幻读 | 同一个事务中多次执行同一个select, 读取到的数据行发生改变。也就是行数减少或者增加了(被其它事务delete/insert并且提交)。 SERIALIZABLE要求解决幻读问题 |
可重复读 | 同一个事务中多次执行同一个select, 读取到的数据没有发生改变(一般使用MVCC实现); RR隔离级别要求达到可重复读的标准 |
不可重复读 VS 幻读
- 不可重复读。重点在update,同样的条件的select, 你读取过的数据, 再次读取出来发现值不一样了
- 幻读。重点在insert或delete,同样的条件的select, 第1次和第2次读出来的行数不一样
小结
- 从结果上来看, 两者都是为多次读取的结果不一致。但如果你从实现的角度来看, 它们的区别就比较大:
- 对于前者, 在RC下只需要锁住满足条件的记录,就可以避免被其它事务修改,也就是 select for update, select in share mode; RR隔离下使用MVCC实现可重复读;
- 对于后者, 要锁住满足条件的记录及所有这些记录之间的gap,也就是需要gap lock。
隔离级别
隔离级别 | 定义 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
读未提交 | RU(READ UNCOMMITTED) | 可以读取未提交的数据,未提交的数据称为脏数据,所以又称脏读 | Y | Y | Y |
读提交 | RC(READ COMMITTED) | 只能读取已经提交的数据 | N | Y | Y |
可重复读 | RR(REPEATABLE READ) | 同一个事务中多次执行同一个select,读取到的数据没有发生改变 | N | N | Y |
串行化 | (SERIALIZABLE) | 读写操作相互阻塞 | N | N | N |
-
小结
- 可重复读RR并不能完全避免幻读,因为在A、B事务中通过
A.select - B.insert - A.update - A.select
顺序,就会出现幻读 - 只有串行化唯一把脏读、不可重复读、幻读都解决
- 可重复读RR并不能完全避免幻读,因为在A、B事务中通过
数据库的默认隔离级别
- ORACLE。默认RC,且只实现了RC 和 SERIALIZABLE
- MySQL。默认RR。
- 其他数据库。默认RC
间隙锁(Gap Lock)
- 为了解决幻读问题,MySQL引入了间隙锁,间隙锁锁的就是两个值之间的空隙,且在RR和S级别下才有
- Next-Key Lock = Gap Lock + Record Lock(行锁)
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);
行锁的冲突关系是另外一个行锁
间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作
- Session A:因为c=7这行记录不存在,所以加的是间隙锁(5,10)
- Session B:同样是在间隙加锁,
RC VS RR
-
一致性读的区别。RC在一个事务中可以读到另外一个事务已经提交的数据。RR是快照读,在一个事务中从第一个select触发建立快照,直到事务结束都保持一致,不管其他事务是否有提交都不会改变。
-
锁的区别。主要体现在并发性上,RC的并发要好于RR,原因如下
- 间隙锁(gap lock)。RR也可以通过间隙锁来解决幻读问题,RC只解决了脏读和不可重复读
- RC会在
where
条件过滤之后,将不符合条件的记录上的行锁释放掉。而RR即使不符合where
条件的记录,也不会释放行锁和gap lock
-
主从复制区别。
- RC不支持 statement 格式的bin log,因为该格式的复制,会导致主从数据的不一致;只能使用 mixed 或者 row 格式的bin log; 这也是为什么MySQL默认使用RR隔离级别的原因。复制时,我们最好使用:binlog_format=row
-
半一致性读。