事务的基本概念以及隔离级别
1. 事务的基本概念
1.1 事务
所谓事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的单位。例如,在关系型数据库中,一个事务可以是一条SQL语句、一组SQL语句或者整个程序。
在SQL中,定义事务的语句一般有三条:
- BEGIN TRANSACTION
- COMMIT
- ROLLBACK
事务通常是以BEGIN TRANSACTION开始,以COMMIT或者ROLLBACK结束。COMMIT表示提交,即提交事务的所有操作。具体而言是指将事务中所有对数据库的更新写道磁盘上的物理数据库中去,事务正常结束。ROLLBACK表示回滚,即在事务运行的过程中发生故障,十五不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。
2. 事务的ACID特性
事务具有四个特性:原子性、一致性、隔离性和持续性。
- 原子性
事务是数据库的逻辑工作单位,事务中包括的诸多操作被看作一个整体的业务单元,这些单元中的操作要么全部成功,要么全部失败,不会出现过部分失败、部分成功的场景。 - 一致性
事务在完成时,必须使所有的数据都保持一致状态,在数据库中所有的修改都基于事务,保证了数据的完整性。 - 隔离性
一个事务的执行不能被其他事务干扰。即一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 - 持久性
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3. 隔离级别
第一类丢失更新:对于一个事务回滚另外一个事务提交而引起的数据不一致的情况,我们称为第一类丢失更新
第二类丢失更新:多个事务提交引发的丢失更新称为第二类丢失更新。
- 未提交读(read uncommited)
未提交读是最低的隔离级别,其含义是允许一个事务读取另外一个事务没有提交的数据。未提交读是一种危险的隔离级别,但是它的优点在于并发能力高,它的最大坏处是出现脏读。
时刻 | 事务1 | 事务2 | 备注 |
---|---|---|---|
T0 | … | … | 商品初始化库存为2 |
T1 | 读取库存为2 | ||
T2 | 扣减库存 | 库存为1 | |
T3 | 扣减库存 | 库存为0,读取事务1未提交的库存数据 | |
T4 | 提交事务 | 库存保存为0 | |
T5 | 回滚事务 | 因为第一类丢失更新已经克服,所以不会回滚为2,库存为0,结果错误 |
- 读写提交(read commited)
读写提交隔离级别是指一个事务只能读取另外一个事务已经提交的数据,不能读取未提交的数据。
时刻 | 事务1 | 事务2 | 备注 |
---|---|---|---|
T0 | … | … | 商品初始化库存为2 |
T1 | 读取库存为2 | ||
T2 | 扣减库存 | 库存为1 | |
T3 | 扣减库存 | 库存为1,读取不到事务1未提交的库存数据 | |
T4 | 提交事务 | 库存为1 | |
T5 | 回滚事务 | 因为第一类丢失更新已经克服,所以不会回滚为2,库存为1,结果正确 |
读写提交也会产生下面的问题。
时刻 | 事务1 | 事务2 | 备注 |
---|---|---|---|
T0 | … | … | 商品初始化库存为1 |
T1 | 读取库存为1 | ||
T2 | 扣减库存 | 事务未提交 | |
T3 | 读取库存为1 | 认为可以扣减 | |
T4 | 提交事务 | 库存为0 | |
T5 | 扣减库存 | 失败,因为此时库存为0,无法扣减 |
以上的现象称为不可重复读,不可重复读是指事务1读取事务后,事务2执行更新操作,使得事务1无法再现前一次读取结果。这就是读写提交的不足。为了克服这个不足,数据库的隔离级别还提出了可重复读的隔离级别,他能消除不可重读的问题。
- 可重复读
可重复读的目标是克服读写提交中出现的不可重复读的现象。因为在读写提交时可能会出现一些值的变化,影响当前事务的执行。
时刻 | 事务1 | 事务2 | 备注 |
---|---|---|---|
T0 | … | … | 商品初始化库存为1 |
T1 | 读取库存为1 | ||
T2 | 扣减库存 | 事务未提交 | |
T3 | 尝试读取库存 | 不允许读取,等待事务1提交 | |
T4 | 提交事务 | 库存为0 | |
T5 | 读取库存 | 库存为0,无法扣减 |
此时会引发新的问题出现,这就是幻读,如下表所示。
时刻 | 事务1 | 事务2 | 备注 |
---|---|---|---|
T1 | 读取库存为50件 | 商品库存初始化为100,现在已经销售50笔,库存为50件 | |
T2 | 查询交易记录,50笔 | ||
T3 | 扣减库存 | ||
T4 | 插入一笔交易记录 | ||
T5 | 提交事务 | 库存49件,交易记录51笔 | |
T6 | 打印交易记录,51笔 | 这里与查询的不一致,在事务2看来有一笔是虚幻的,与之前查询不一致 |
以上便是幻读现象。
-
串行化
串行化是数据库中最高的隔离级别,它会要求所有的SQL按照顺序执行,这样就可以科夫上述隔离界别出现的各种问题。所以它能够保证数据的一致性。 -
使用合理的隔离级别
隔离界别和可能发生的现象
项目类型 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | √ | √ | √ |
读写提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
对于Oracle默认的隔离级别未读写提交,MySQL则是可重复读。
参考文献
- 《数据库系统概述》
- 《深入浅出SpringBoot》