一 事务的隔离级别
从低到高为:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串形(Serializable),后三种分别可以解决脏读、不可用重复读、幻读的问题。
二 MySQL为什么要选择RR作为默认隔离级别
1 RC隔离级别太低,不利于数据安全性;Serializable隔离级别太高,不利于接口并发。
2 MySQL的RR隔离机制,使用了间隙锁(GAP Lock),配合MVCC机制可以在一定程度上避免幻读(如果要完全解决幻读的问题,还是需要使用Serializable)。因此,使用RR,不仅可以解决脏读、不可重复读,也可以在一定程度上解决幻读的问题。
tips:在MVCC机制下,事务都是进行快照读,因此大部分情况下不会出现幻读。但是,如果在事务A进行了快照读后,事务B插入新数据并提交,此时事务A可以update B新插入的这条数据成功,也是发生了幻读。因此,要想彻底解决幻读,需要采用Serializable隔离级别。
3 为了兼容binlog的statement格式。详见下文。
三 binlog的格式
binlog用于主从之间同步数据,其中存储的数据有三种格式:statement、row、mixed。
statement格式存储的就是执行的sql语句,使用简单、占用存储空间小
row格式存储的是每行修改前后的数据,恢复数据精确,但是占用空间大
mixed是statement和row格式的折衷,mysql会根据每条sql命令决定采用哪种格式存储。
statement格式有个问题,举个例子,采用RC的隔离级别,两个事务A、B,A事务执行删除小于100的行,B事务执行插入一条值为90的行。A先开始执行事务,中途B再开始,但是B先提交,A最后提交。主库中由于事务A读取不到B未提交的数据,因此最终会把90插入进去。
sessionA | sessionB |
set session transaction isolation level read committed; | |
set autocommiteed=0; | set session transaction isolation level read committed; |
set autocommiteed=0; | |
begin; | begin; |
delete from t where b<100; | |
insert into t values(90); | |
commit; | |
commit; |
根据事务提交的顺序,binlog中就会顺序写入insert、delete两条命令。从库根据binlog同步数据,最终的结果就是把B插入的数据90也删了,导致主从不一致。
而如果使用RR的隔离级别,A会在更新数据的时候加间隙锁(GAP Lock)和临键锁(Next Key Lock),事务B会等待A提交或者回滚完成之后再执行,就解决了这个问题。因此Mysql默认使用RR作为事务隔离机制。