深入解析数据库事务的奥秘

发表时间: 2022-12-26 11:30

事务处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行。如果没有错误发生,整组语句提交给数据库表;如果发生错误,则进行回退,将数据库恢复到某个已知且安全的状态。

事务的四大特性(ACID)

原子性(Atomicity): 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。原子性仅仅意味着它不能被分解,因此事务就是一个最小的执行单元。

假如A转500块钱给B。原子性保证了A的余额将被扣除500,B的余额将增加500。整个过程要么全部成功,要么全部失败。不能出现A的余额扣除成功,B的余额增加失败的情况。

一致性(Consistency): 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。事务开始之前和事务结束以后,数据不会被破坏。

假如A转500块钱给B。不管成功与否,转入前和转入后两个人的余额和是一致的。

隔离性(Isolation): 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

假设A同时向B、C各转500块钱。如果两个操作同时进行,T1读取A的余额为1000,同时T2读取A的余额也为1000,分别扣除500发给B和C,然后两者都将更新A的余额为500。这样就出现问题了,因为A余额应该是0。

持久性(Durability): 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变.就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

例如,A有1000元,它把500转给了B。每次我们查询A的余额时,我们应该得到最新的值,不能丢失这些细节。

事务并发存在的问题

假如有以下表数据:

id

name

balance

1

John

1000

2

Lucy

1000

脏读:

现在有两个事务A、B:

  • 事务A查询John的余额1000
  • 事务B先扣减John的余额,扣了100
  • 最后A 读到的是扣减后的余额

执行顺序

事务A

事务B

1

begin


2


begin

3


更新John余额,减100

4

查询John余额


5

返回John余额为900


事务A、B交替执行,事务A被事务B干扰到了,因为事务A读取到事务B未提交的数据,这就是脏读

不可重复读:

假设现在有两个事务A和B:

  • 事务A先查询Lucy的余额,查到结果是1000
  • 事务B 对Lucy的账户余额进行扣减,扣去100后,提交事务
  • 事务A再去查询Lucy的账户余额发现变成了900

执行顺序

事务A

事务B

1

begin


2

查询Lucy余额1000


3


begin

4


更新Lucy余额

5


Lucy余额减100

6


commit

7

查询Lucy余额900


8

commit


在事务A范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读

幻读

假设现在有两个事务A、B:

  • 事务A先查询id大于2的账户记录,得到记录id=2和id=3的两条记录
  • 这时候,事务B开启,插入一条id=4的记录,并且提交了
  • 事务A再去执行相同的查询,却得到了id=2,3,4的3条记录了。

执行顺序

事务A

事务B

1

begin


2

查询表中所有数据


3

返回ID为1,2两条记录


4


begin

5


插入id=3,name=jim,balance=100

6


commit

7

查询表中所有数据


8

返回ID为1,2,3两条记录


事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,并静悄悄地提交,然后事务A再次查询相同的范围,两次读取得到的结果集不一样了,这就是幻读

事务隔离等级

读未提交(Read Uncommitted)

事务的修改,即使没有提交,对其他事务也都是可见的。这种等级下会出现脏读、不可重复读、幻读。

读已提交(READ-COMMITTED)

事务读取已提交的数据,大多数数据库的默认隔离级别。当一个事务在执行过程中,数据被另外一个事务修改,造成本次事务前后读取的信息不一样。这种等级下会出现、不可重复读、幻读。

可重复读(REPEATABLE-READ )

这个级别是MySQL的默认隔离级别,它解决了脏读的问题,同时也保证了同一个事务多次读取同样的记录是一致的,但这个级别还是会出现幻读的情况。

序列化(SERIALIZABLE )

最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。