一、背景介绍

最近在进行项目开发的过程中,由于ORM映射是我们自己手动实现的,其中涉及到了事务的开启和提交,以及在高并发的情况下如何保持数据的正确性。所以通过项目驱动,再次对事务进行学习和实践。

二、 什么是事物(Transaction)

一般是指要做的或者所做的事,在计算机语言中指访问并可能更新数据库中数据项的一个程序执行单元,在关系型数据库中,一个事物可以是一条SQL语句,一组SQL语句或整个程序。

三、事物的4个属性

原子性、一致性、隔离性、持久性、统称为ACID特性

  1. 原子性(atomicity)一个事物是一个不可再分的单元,要么都做,要么都不做。不会结束在中间某个环节。事物在发生错误的时候会进行回滚到没有开始事物之前的状态。

  2. 一致性(consistency):事务在执行前后,数据库必须处于一致的状态。这意味着事务开始之前和结束之后,数据库的完整性约束、关联关系以及业务规则都必须得到保持。

  3. 隔离性(isolation):一个事物的执行不能被其他事物干扰。一个事物内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事物之间不能互相干扰。

  4. 持久性(durability):指一个事物一旦提交他对数据中心的数据改变就应该是永久性的,即使在系统发生故障或重启后也能够保持。数据库系统将事务的结果记录到非易失性存储器(如磁盘)中,以确保数据的持久性。

四、事物的隔离级别

为什么要有事务的隔离级别,用于解决那些问题?

在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。

以下是一些常见的并发问题和隔离级别可以解决的问题:

脏读

一个事务读取到了另一个未提交事务所做的修改。脏读可能导致读取到不一致或无效的数据。
举例:
假设有两个事务,T1和T2,操作同一个银行账户的余额数据。
T1事务进行如下操作:
读取账户余额为$100。
T2事务开始并修改账户余额为$200。
T1事务再次读取账户余额,此时获得了T2未提交的修改结果,得到$200。
如果此时T2事务回滚,T1事务就读取到了脏数据,即未提交的修改结果,导致脏读的发生。

不可重复读

不可重复读指的是在同一个事务中,多次读取同一行数据时,得到的结果不一致。这是由于其他事务在两次读取之间对数据进行了修改或删除。
举例:
例如,事务T1首先读取某一行数据,然后事务T2对该行进行修改并提交,接着事务T1再次读取该行,发现数据已经发生了变化。

幻读

幻读指的是在同一个事务中,多次查询同一个条件下的结果集时,得到的结果行数不一致。这是由于其他事务在两次查询之间插入、更新或删除了符合条件的数据,导致结果集的行数发生了变化。

举例:
例如,事务T1首先查询某个条件下的结果集,然后事务T2插入了满足该条件的新数据并提交,接着事务T1再次查询该条件下的结果集,发现结果集的行数发生了变化。

不可重复度与幻读的区别:
不可重复读关注的是在同一事务中读取相同数据的结果不一致性,因为其他事务对数据进行了修改或删除。

幻读关注的是在同一事务中多次查询同一条件下的结果集,结果集的行数不一致,因为其他事务插入、更新或删除了符合条件的数据

更新丢失

丢失更新指的是两个事务同时修改同一行数据,其中一个事务的修改被另一个事务覆盖,导致数据的更新丢失。

举例:
假设有两个事务,T1和T2,操作同一个商品库存数据。
T1事务进行如下操作:
读取商品A的库存数量为10。
T2事务开始并读取商品A的库存数量为10。
T1事务将商品A的库存数量增加5。
T2事务将商品A的库存数量减少3。
T1事务提交,将商品A的库存更新为15。
T2事务提交,将商品A的库存更新为7,覆盖了T1的修改结果。
结果是,T1事务的更新被T2事务的更新覆盖,导致T1事务的修改丢失,从而导致了更新丢失的情况。

五、解决方案

为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。

未授权读取

也称为读未提交(Read Uncommitted):读未提交是最低的隔离级别,允许事务读取其他事务尚未提交的数据。
在这个级别下,事务可以读取到其他事务进行的未提交修改,可能导致脏读问题。
这种级别很少被使用,因为它没有提供真正的并发控制机制,可能导致数据不一致。

授权读取

也称为读提交(Read Committed):在这个级别下,事务读取的数据会在其他事务修改并提交后才能看到。
读已提交级别解决了脏读问题,但是可能仍然会发生不可重复读和幻读。

可重复读取(Repeatable Read):

在这个级别下,事务会通过锁定读取的数据,防止其他事务对数据进行修改。
味着在一个事务读取数据时,其他事务是允许对数据进行查询的。三四其他事务将无法对该数据进行修改,直到读取事务完成并提交或回滚。
可重复读解决了脏读和不可重复读问题,但是幻读仍然可能发生。

序列化(Serializable)

序列化(串行化)(Serializable):
串行化是最高的隔离级别,通过强制事务串行执行来解决并发问题。
在这个级别下,事务依次执行,确保了事务的隔离性和一致性。
串行化可以避免脏读、不可重复读和幻读问题,但是代价是牺牲了并发性能,因为事务需要依次执行。

六、总结

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

06-27 12:03