事务管理理论知识
事务概念
事务就是对数据库的一次操作,要么全部成功,要么全部失败。事务是最小的逻辑执行单元,也是数据库并发控制的基本单位。其作用就是确保数据的准确性。
事务四大特性
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。保证事务内的操作是不可分割的。
一致性(Consistency)
一致性是指事务执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。比如转账,假设用户A和用户B两者的钱加起来一共是2000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是2000,不可能出现其他情况。
隔离性 (Isolation)
隔离性是指并发执行的事务之间不能相互影响。比如写一条update语句和delete语句,而且同时操作一条记录,如果不加控制那就会出现各种问题。
持久性 (Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。比如执行insert语句后,就必须要有数据写入磁盘。
总之,这四个特性是事务管理的基石,其中,原子性是基础,隔离性是手段,持久性是目的,真正的老大就是一致性。其中最难的是隔离性,因为事务是原子操作,但事务与事务之间可以并发执行,因此要达到一致性就要求事务与事务之间进行隔离,那么事务如果不隔离直接并发执行会造成什么后果呢?
事务并发问题
脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
时间 | 事务 A(存款) | 事务 B(取款) |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询余额(500 元) | |
T4 | 取出 500 元(余额 0 元) | |
T5 | 查询余额(0 元) | |
T6 | 撤销事务(余额恢复为 500 元) | |
T7 | 存入 100 元(余额 100 元) | |
T8 | 提交事务 |
余额应该为 600 元才对!请看 T5 时间点,事务 A 此时查询余额为 0 元,这个数据就是脏数据,它是事务 B 造成的,明显事务没有进行隔离,渗过来了,乱套了。
不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据结果,这是由于在查询间隔,被另一个事务修改并提交了。
时间 | 事务 A(存款) | 事务 B(取款) |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询余额(500 元) | |
T4 | 查询余额(500 元) | |
T5 | 取出 500 元(余额 0 元) | |
T6 | 提交事务 | |
T7 | 查询余额(0 元) |
事务 A 其实除了查询了两次以外,其他什么事情都没有做,结果钱就从 1000 变成 0 了,这就是重复读了。可想而知,这是别人干的,不是我干的。其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,所以事务 A 再次读取自然就发生了变化。这种现象也正常,但在某些场景下确实不允许的(比如电商中扣减库存)。
虚读(幻读)
幻读是事务非独立执行时发生的一种现象,即在一个事务读的过程中,另外一个事务可能插入了新数据记录,影响了该事务读的结果。
时间 | 事务 A(统计总存款) | 事务 B(存款) |
---|---|---|
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 统计总存款(1000 元) | |
T4 | 存入 100 元 | |
T5 | 提交事务 | |
T6 | 统计总存款(1100 元) |
归纳一下,以上提到了事务并发所引起的跟读取数据有关的问题,各用一句话来描述一下:
- 脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。
- 不可重复读:事务 A 读取了事务 B 已提交的更改数据。
- 幻读:事务 A 读取了事务 B 已提交的新增数据。
第一条是坚决抵制的,后两条在大多数情况下可不作考虑。正因为事务并发时会出现问题,因此数据库专家们就提出了解决方案即事务的隔离级别。
事务隔离级别
不同数据库的事务隔离级别不尽相同。MySQL数据库支持下面的四种隔离级别,并且5.5以后默认为 Repeatable read 级别,之前是 Read committed ;而在Oracle数据库中,只支持Serializable 级别和 Read committed 这两种级别,并且默认为 Read committed 级别。MySQL数据库为我们提供了四种隔离级别,分别为:
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ_UNCOMMITTED | 允许 | 允许 | 允许 |
READ_COMMITTED | 禁止 | 允许 | 允许 |
REPEATABLE_READ | 禁止 | 禁止 | 允许 |
SERIALIZABLE | 禁止 | 禁止 | 禁止 |
# 查询隔离级别
select @@tx_isolation;
# 设置隔离级别
set [glogal | session] transaction isolation level 隔离级别名称;