总结一下事务在使用过程中的一些坑。
在介绍之前,先普及一些基础知识
一、基础知识
事务隔离等级
一般事务的隔离等级有Read uncommitted,Read committed,Repeatable read,Serializable。其中隔离程度越来越严格,到了Serializable已经不支持并发事务了。
Read uncommitted
会造成脏读,可重复读,幻读。什么是脏读,看一下关键词uncommitted,英文很直白。网上很多抄来抄去的文章很多把脏读和不可重复读混淆。特别是乐观锁的文章。脏读也就是说读取到别人未提交的事务中的改动。举个例子:
原数据 id=1 value=6
事务A 事务B
修改id=1的数据value=8
读取id=1的数据value=8
commit
Read committed
会造成可重复读,幻读。read committed字面上已经表明了只能读取到事务提交后的数据,所以避免了脏读。什么是可重复读,可重复读是指在事务中前后读取两次同一数据,由于在此期间,存在其他事务修改该数据并提交,造成前后读取不一致。举个例子
原数据 id=1 value=6
事务A 事务B
读取id=1的数据value=6
修改id=1的数据value=8
commit
读取id=1的数据value=8
Repeatable read
会造成幻读。Repeatable read字面上也表明了可以在事务中重复读取。比如mysql中的实现方式是事务开始后的select将会把结果存在临时数据中。事务中再次读取时直接从临时数据中取值,因此可以重复读取。但是这只保证了update操作,对于insert操作,则会产生幻读。举个例子:
原数据 id=1 value=6
事务A 事务B
读取id=1的数据 value=6
新增一行数据id=1 value=8
commit
读取id=1的数据 value=6
value=8
这样得到了两条数据,则是幻读。
Serializable
不允许并发事务,不会产生任何并发问题。
乐观锁
乐观锁和之前的事务隔离等级一样,也是为了解决一些并发问题。乐观锁解决的问题如下:
不可重复读
从之前的事务隔离等级已经知道Repeatable read则可以解决不可重复读,乐观锁和它之间还是有区别的。
如果在一个事务中防止读取另一个已提交事务造成的重复读,在这一点上和Repeatable read的区别是,如果是Repeatable read,还是可以获得事务开始时的值,而乐观锁你只知道值变了,无法获得原始值。
更新丢失
乐观锁的作用范围超过了事务。考虑如下场景,用户A打开了修改界面,修改界面读取当前数据显示。然后用户A去做其他事情了。这时候用户B修改了同样的数据。用户A回来继续修改,提交事务则覆盖了用户B的修改。
只读事务
在数据库层面,只读事务的作用也是解决不可重复读的问题,并且不允许该事务中存在除了查询以外的操作。
事务传播等级
spring提出的概念,这里只介绍容易混淆的等级
PROPAGATION_REQUIRED