事务
文章目录
事务(Transaction)是由一系列对系统中数据进⾏访问与更新的操作所组成的⼀个程序执行逻辑单元。
1 事务的语法
- 开启事务
- start transaction;
- begin;
- 提交事务(使得当前的修改确认)
- commit;
- 回滚事务(使得当前的修改被放弃)
- rollback;
2 事务的ACID特性
-
原⼦性(Atomicity)
事务的原⼦性是指事务必须是⼀个原子的操作序列单元。事务中包含的各项操作在⼀次执⾏过程中,只允许出现两种状态之一。
(1)全部执行成功
(2)全部执行失败 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。
事务执⾏过程中出错, 会回滚到事务开始前的状态,所有的操作就像没有发⽣一样。也就是说事务是⼀个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
-
⼀致性(Consistency)
事务的一致性是指事务的执⾏不能破坏数据库数据的完整性和一致性,一个事务在执⾏之前和执行之后,数据库都必须处以⼀致性状态。
比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,⽽B账户没有加钱。
-
隔离性(Isolation)
事务的隔离性是指在并发环境中,并发的事务是互相隔离的。
也就是说,不同的事务并发操作相同的数 据时,每个事务都有各自完整的数据空间。
⼀个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
-
持久性(Duration)
事务的持久性是指事务⼀旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态
3 事务的并发问题
-
脏读:
读取到了没有提交的数据, 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。 -
不可重复读:
同⼀条命令返回不同的结果集(主要在update中体现),事务 A 多次读取同一数据,事务 B 在事务A 多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致。 -
幻读:
重复查询的过程中,数据就发⽣了量的变化(主要在insert, delete中体现)。
4 事务的隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ_UNCOMMITTED) | 允许 | 允许 | 允许 |
读已提交(READ_COMMITTED) | 禁止 | 允许 | 允许 |
可重复读(REPEATABLE_READ) | 禁止 | 禁止 | 可能会 |
顺序读(SERIALIZABLE) | 禁止 | 禁止 | 禁止 |
4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。 ⼀般数据默认级别是读已提交或可重复读
- 查看当前会话中事务隔离级别
#mysql8之前的语法
#select @@tx_isolation;
#mysql8之后用以下语法查看事务隔离级别
select @@transaction_isolation;
- 设置当前会话中的事务隔离级别
#每个会话需要单独设置隔离级别,默认为可重复读,会话结束,隔离级别失效
set session transaction isolation level read uncommitted; #读未提交,注意read和uncommitted之间没有下划线
#set session transaction isolation level read committed 读已提交
#set session transaction isolation level repeatable read 可重复读
#set session transaction isolation level serializable 顺序读
4.1 读未提交
读未提交,该隔离级别允许脏读取,其隔离级别是最低的。换句话说,如果一个事务正在处理理某一数 据,并对其进⾏了更新,但同时尚未完成事务,因此还没有提交事务;而与此同时,允许另一个事务也能够访问该数据。
脏读示例
时间点 | 事务A(存款) | 事务B(取款) |
---|---|---|
T1 | 开始事务 | 一一 |
T2 | 一一 | 开始事务 |
T3 | 一一 | 查询余额(1000元) |
T4 | 一一 | 取出1000元(余额0元) |
T5 | 查询余额(0元) | 一一 |
T6 | 一一 | 撤销事务 |
T7 | 存入500元(余额500元) | 一一 |
T8 | 提交事务 | 一一 |
- 打开两个会话窗口,设置事务隔离级别为read uncommitted;
- 创建测试表
create table money(
userid int,
usermoney int);
insert into money values(1,1000),(2,1000);
select * from money;
- 两个会话都能查看这张表
- 两个会话分别开启事务
- B事务查询余额并取出1000元
- A事务查询余额
- B事务进行回滚
- A事务存入500,并提交事务,再次查询,出现结果与目标不一致的情况。
4.2 读已提交
读已提交是不同的事务执行的时候只能获取到已经提交的数据。 这样就不会出现上面的脏读的情况了。 但是在同一个事务中执行同一个读取,结果不一致
- 设置会话隔离级别为读已提交(首先看解决脏读问题)
- 开启事务
- 事务B查询余额并取出1000元
- 事务A查询余额
- 事务B回滚,事务A存入500,提交,并查询,查询到的结果和目标是一致的,所以解决了脏读的问题
不可重复读问题示例
时间点 | 事务A(存款) | 事务B(取款) |
---|---|---|
T1 | 开始事务 | 一一 |
T2 | 一一 | 开始事务 |
T3 | 一一 | 查询余额(2000元) |
T4 | 查询余额(2000元) | 一一 |
T5 | 一一 | 取出1000元(余额1000元) |
T6 | 一一 | 提交事务 |
T7 | 查询余额(1000元) | 一一 |
T8 | 提交事务 | 一一 |
-
开启事务
-
事务A,B分别查询余额
- 事务B取出1000元,提交事务
- 事务A查询余额,在同一个事务里,两次查询结果不一致,出现不可重复读问题
4.3 可重复读示例
可重复读就是保证在事务处理理过程中,多次读取同一个数据时,该数据的值和事务开始时刻是一致的。 因此该事务级别限制了不可重复读和脏读,但是有可能出现幻读的数据。
- 设置两个会话的隔离级别为repeatable read(解决了不可重复读问题)
- 两个会话分别开启事务
- 事务A,B分别查询余额
- 事务B取出1000元,提交事务
- 事务A查询余额,余额还是1000元,解决了不可重复读问题
- 事务A提交事务,再次查询,结果变为更改后的值
幻读产生情况
幻读就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。 诡异的更新事件
时间点 | 事务A(存款) | 事务B(取款) |
---|---|---|
T1 | 开始事务 | 一一 |
T2 | 查询当前所有数据 | 开始事务 |
T3 | 一一 | 插入一条数据 |
T4 | 一一 | 提交事务 |
T5 | 进行范围修改 | 一一 |
T6 | 查询当前所有数据 | 一一 |
T7 | 提交事务 | 一一 |
- 两个会话分别开启事务
- 事务A查询当前余额
- B事务插入一条数据,并提交事务
- A事务进行范围修改,并查询数据,在一个事务中本来只想修改两条数据,结果发现多了一条数据,这就是幻读
4.4 顺序读示例
顺序读是最严格的事务隔离级别。它要求所有的事务排队顺序执⾏行行,即事务只能一个接一个地处理,不能并发。
- 设置隔离级别为serializable
- 事务A开启事务,并查询数据
- 事务B开启事务,并执行插入语句,可以看见事务B并未执行,而在排队等待中
- 只有当A事务提交或回滚之后,B事务才会执行
5 不同隔离级别锁的情况
- 读未提交(RU): 有行级的锁,没有间隙锁。它与RC的区别是能够查询到未提交的数据。
- 读已提交(RC):有行级的锁,没有间隙锁,读不到没有提交的数据。
- 可重复读(RR):有行级的锁,也有间隙锁,每次读取的数据都是一样的,并且没有幻读的情况。
- 序列化(S):有行级锁,也有间隙锁,读表的时候,就已经上锁了