数据库事务ACID特性
数据库事务正确执行的4个基础要素是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
这里的原子性、一致性和持久性都比较好理解,而隔离性就不一样了,它涉及了多个事务并发的状态。首先多个事务并发会产生数据库丢失更新的问题,其次隔离性又分为多个层级。
这里的原子性、一致性和持久性都比较好理解,而隔离性就不一样了,它涉及了多个事务并发的状态。首先多个事务并发会产生数据库丢失更新的问题,其次隔离性又分为多个层级。
隔离级别
脏读是最低的隔离级别,其含义是允许一个事务去读取另一个事务中未提交的数据。以丢失更新的消费为例进行说明,如表所示。


为了克服脏读,SQL标注提出了第二个隔离级别――读/写提交。所谓读/写提交,就是说一个事务只能读取另一个事务已经提交的数据。依旧以丢失更新的夫妻消费为例,如表所示。




不可重复读
为了克服不可重复读带来的错误,SQL标准又提出了一个可重复读的隔离级别来解决问题。注意,可重复读这个概念是针对数据库同一条记录而言的,换句话说,可重复读会使得同一条数据库记录的读/写按照一个序列化进行操作,不会产生交叉情况,这样就能保证同一条数据的一致性,进而保证上述场景的正确性。但是由于数据库并不是只能针对一条数据进行读/写操作,在很多场景,数据库需要同时对多条记录进行读/写,这个时候就会产生下面的情况,如表所示。


为了克服幻读,SQL标准又提出了序列化的隔离级别。它是一种让SQL按照顺序读/写的方式,能够消除数据库事务之间并发产生数据不一致的问题。关于各类的隔离级别和产生的现象如表所示。


选择隔离级别和传播行为
选择隔离级别的出发点在于两点:性能和数据一致性,下面展开论述。
在互联网应用中,不但要考虑数据库数据的一致性,而且要考虑系统的性能。一般而言,从脏读到序列化,系统性能直线下降。因此设置高的级别,比如序列化,会严重压制并发,从而引发大量的线程挂起,直到获得锁才能进一步操作,而恢复时又需要大量的等待时间。因此在购物类的应用中,通过隔离级别控制数据一致性的方式被排除了,而对于脏读风险又过大。在大部分场景下,企业会选择读/写提交的方式设置事务。这样既有助于提高并发,又压制了脏读,但是对于数据一致性问题并没有解决,后面会详细讨论如何去克服这类问题。对于一般的应用都可以使用@Transactional方法进行配置
代码清单:使用读/写提交隔离级别
当然也会有例外,并不是说所有的业务都在高并发下完成,当业务并发量不是很大或者根本不需要考虑的情况下,使用序列化隔离级别用以保证数据的一致性,也是一个不错的选择。总之,隔离级别需要根据并发的大小和性能来做出决定,对于并发不大又要保证数据安全性的可以使用序列化的隔离级别,这样就能够保证数据库在多事务环境中的一致性
代码清单:使用序列化隔离级别
只是这样的代码会使得数据库的并发能力低下,在抢购商品的场景下出现卡顿的情况,所以在高并发的场景下这段代码并不适用
在实际工作中,注解@Transactional隔离级别的默认值为Isolation.DEFAULT,其含义是默认的,随数据库默认值的变化而变化。因为对于不同的数据库而言,隔离级别的支持是不一样的。比如MySQL可以支持4种隔离级别,而默认的是可重复读的隔离级别。而Oracle只能支持读/写提交和序列化两种隔离级别,默认为读/写提交,这些是在工作中需要注意的问题。
在互联网应用中,不但要考虑数据库数据的一致性,而且要考虑系统的性能。一般而言,从脏读到序列化,系统性能直线下降。因此设置高的级别,比如序列化,会严重压制并发,从而引发大量的线程挂起,直到获得锁才能进一步操作,而恢复时又需要大量的等待时间。因此在购物类的应用中,通过隔离级别控制数据一致性的方式被排除了,而对于脏读风险又过大。在大部分场景下,企业会选择读/写提交的方式设置事务。这样既有助于提高并发,又压制了脏读,但是对于数据一致性问题并没有解决,后面会详细讨论如何去克服这类问题。对于一般的应用都可以使用@Transactional方法进行配置
代码清单:使用读/写提交隔离级别
@Autowired private RoleDao roleDao = null; //设置方法为读/写提交的隔离级别 @Transaction(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) public int insertRole(Role role) { return roleDao.insert(role); }
代码清单:使用序列化隔离级别
@Autowired private RoleDao roleDao = null; //设置方法为序列化的隔离级别 @Transaction(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE) public int insertRole(Role role) { return roleDao.insert(role); }
只是这样的代码会使得数据库的并发能力低下,在抢购商品的场景下出现卡顿的情况,所以在高并发的场景下这段代码并不适用
在实际工作中,注解@Transactional隔离级别的默认值为Isolation.DEFAULT,其含义是默认的,随数据库默认值的变化而变化。因为对于不同的数据库而言,隔离级别的支持是不一样的。比如MySQL可以支持4种隔离级别,而默认的是可重复读的隔离级别。而Oracle只能支持读/写提交和序列化两种隔离级别,默认为读/写提交,这些是在工作中需要注意的问题。
传播行为
传播行为是指方法之间的调用事务策略的问题。在大部分的情况下,我们都希望事务能够同时成功或者同时失败。但是也会有例外
在Spring中传播行为的类型,是通过一个枚举类型去定义的,这个枚举类是org.springframework.transaction.annota-tion.Propagation,它定义了如表所列举的7种传播行为。
在Spring中传播行为的类型,是通过一个枚举类型去定义的,这个枚举类是org.springframework.transaction.annota-tion.Propagation,它定义了如表所列举的7种传播行为。


文章来源:ssm 13.4,13.5