传统的数据库定义了四种隔离级别:
1. Read Uncommitted
2.Read Committed
3.Repeatable Read
4.Serializable
sql定义中Repeatable Read可以保证一个事务中多次读取同一条记录时,看到的结果都是一样的,但是它存在幻读(Phantoms)的问题.
其中Serializable是目前sql定义中最严格的隔离级别,即:串行事务级里面的任何并发操作保证他们的执行效果就像以相同顺序一次执行完的一样.也就是说在Serializable级别中,才做到了事务完全互不影响.但是因为实现Serializable隔离级别对数据库性能影响较大,所以往往会采取折衷的方式,使用RR或者RC隔离级别,并配上业务自身的一些控制逻辑.
snapshot isolation
但是随着技术的不断发展,上面4种已经不能完全包含所有的隔离情况,比如后来出现的snapshot isolation(快照隔离).
快照隔离多采用mvcc实现, 我们会记录事务开始的时间点,将这个时间点之前的数据看作是一个快照,所有读操作都只读取该时间点之前的数据.当然本次事务中update/insert/delete的操作对本次事务也是可见的.但是该事务之后新的事务所产生的数据对本次事务是不可见的(并发场景下).
快照隔离中读操作不涉及锁操作,所以一定程度了提高了性能.但是快照隔离引出了一个新的问题:write skew
write skew
什么是write skew?
假设某个业务需要满足约束: x+y<100
最开始x=30, y=10,符合约束规则,现在有两个事务T1和T2,它们观察到x=30, y=10,并且开始执行各自的流程
首先它们都先读取各自需要的数据,这个过程中并没有任何的修改操作,并且采用快照读,不涉及加锁操作.它们都读取到x和y分别为30和10
之后T1首先将y设为60,从T1的角度来看,x y目前满足约束.虽然T1写数据是需要获取锁的,但是在此之前T2已经获取了y等于10.所以此时T2依然认为y为10
然后T2再将x设置为50,但是从T2的角度来看,x和y分别为50和10,它认为依然符合约束,所以更新了x值为50.但是此时的x和y的真实值分别为50和60,不满足条件约束.
这就是所谓的write skew问题, 造成这种现象的原因就是事务以快照的方式访问数据,并且读不加锁,导致数据的不一致.
note:
snapshot isolation和RR隔离并没有可比性,mysql的RR隔离其实是采用snapshot isolation来实现的,所以mysql中的RR隔离级别不存在幻读(Phantoms)的问题.每个数据库对sql定义的隔离级别实现的程度并不相同,所以还要以具体数据库为准.参考:https://blog.acolyer.org/2016/02/24/a-critique-of-ansi-sql-isolation-levels/
serializable snapshot isolation
为了解决这种问题,后面有人提出了:serializable snapshot isolation串行化快照隔离,该隔离级别有效的解决了snapshot isolation的write skew的问题.
可串行化快照隔离本质上就是在事务开始执行的同时,依据可串行化规则,将其与快照隔离机制相结合,通过所设计的算法机制检测当前事务是否存在不可串行化的操作,是的快照隔离应用在数据他的同时最终到达可串行化的目的,解决write skew的问题.具体可参考:
https://drive.google.com/file/d/0B9GCVTp_FHJIcEVyZVdDWEpYYXVVbFVDWElrYUV0NHFhU2Fv/edit
http://www.zenlife.tk/serializable-snapshot-isolation.md
目前大多传统数据库都是提供了snapshot isolation,并且一部分数据库也实现了serializable snapshot isolation.
而且目前新兴的NewSql-CockroachDB也同时提供了两种snapshot隔离级别.现在分布式数据库往往也采用snapshot isolation作为它们的默认隔离级别.
网友评论