事务定义的是一系列数据库操作的序列,这个序列是一个不可分割的逻辑单元,在其中的操作要么全部完成,要么全部无法完成。Spring事务通过Transactional.isolation属性进行定义,其具体值则存储在Isolation枚举中。Spring对事务隔离级别的定义与数据库隔离级别的定义是完全一致的,因而本文主要从数据库的层面对事务进行讲解

图片

      在事务的定义上,其主要有四大特性:原子性、一致性、隔离性和持久性,简称为ACID,在Spring 事务(Transaction) 四大特性有详细介绍

      关于事务的持久性需要说明的是,从事务的角度能够保证数据能够一致性的保存在磁盘上,即使数据库发生故障也能够从故障中恢复,但是如果是数据库之外的问题,比如RAID卡损坏,自然灾害等,这种问题在数据库层面是无法避免的,其也不属于事务的范畴。事务能够保证数据的高可靠性,但是事务并不能保证系统的高可用行

图片

      事务能够始终保证数据保持在一种一致的状态,但是如果严格按照事务的定义来处理事务,那么事务的执行效率将会很低,因为只有保证了所有事务的串行执行才能保证事务,因而在事务规范中为事务定义了四种隔离级别:Read uncommitted、Read committed、Repeatable read和Serializable。关于这四种隔离级别,其主要区别在于三个点:脏读、不可重复读和幻读。这三个点的主要含义如下

脏读:脏读表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录A,此时该事务还未提交,然后另一个事务尝试读取记录A,这时其是会成功读取到记录A的;

不可重复读:不可重复读表示当前事务对同一记录的两次重复读取结果不一致。比如一个事务首先读取一条记录A,读完之后另一个事务将该记录修改并且成功提交了,然后当前事务再次读取记录A,此时该事务会发现两次读取的结果不一致;

幻读:幻读指的是一个事务在进行一次查询之后发现某个记录不存在,然后会根据这个结果进行下一步操作,此时如果另一个事务成功插入了该记录,那么对于第一个事务而言,其进行下一步操作(比如插入该记录)的时候很可能会报错。从事务使用的角度来看,在检查一条记录不存在之后,其进行插入应该完全没问题的,但是这里却抛出主键冲突的异常

关于事务的四种隔离级别,其主要区别点也就在于是否能够解决这三个问题。这四种事务的隔离级别主要区别如下:

  • Read uncommitted:这是隔离性最低的一种隔离级别,在这种隔离级别下,当前事务能够读取到其他事务已经更改但还未提交的记录,也就是脏读;

  • Read committed:顾名思义,这种隔离级别只能读取到其他事务已经提交的数据,也就解决了脏读的问题,但是其无法解决不可重复读和幻读的问题;

  • Repeatable read:从事务的定义上,这种隔离级别能够解决脏读和不可重复读的问题,但是其无法解决幻读的问题;

  • Serializable:也称为序列化读,这是隔离性最高的一种隔离级别,所有的事务执行(包括查询)都会为所处理的数据加锁,操作同一数据的事务将会串行的等待。

     从事务隔离级别的定义上可以看出,Serializable级别隔离性最高,但是其效率也最低,因为其要求所有操作相同记录的事务都串行的执行。这里需要说明的是,对于MySql而言,其默认事务级别是Repeatable read,虽然在定义上讲,这种隔离级别无法解决幻读的问题,但是MySql使用了一种Next key-lock的算法来实现Repeatable read,这种算法是能够解决幻读问题的。关于Next key-lock算法,在进行查询时,其不仅会将当前的操作记录锁住,也会将查询所涉及到的范围锁住。也就是说,其他事务如果想要在当前事务查询的范围内进行数据操作,那么其是会被阻塞的,因而MySql在Repeatable read隔离级别下就已经具备了Serializable隔离级别的事务隔离性