别总骂我恨铁不成钢,别忘了,铁本来就成不了钢呀,摊手┓( ´∀` )┏
数据库事务的定义:
由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元
一、需要重点掌握的三个方向:
- ACID特性
- 事务隔离级别
- 事务并发引起的问题
1. 事务的语法
事务主要包含三个操作:
- 启动:start transaction 或begin
- 提交:commit
- 回滚:rollback(事务在未提交之前都是可以回滚的,但一旦提交了,就不能回滚)
备注:如果不在sql语句的CRUD前输入begin开启事务,事务会自动提交无法回滚
2. 事务的特性
- 原子性(Atomicity):一项操作的执行过程,不是全部执行成功,就是全部执行失败
- 一致性(Consistency):事务的执行不能破坏数据库数据的完整性和一致性。比如:A账户向B账户赚钱,不能A扣钱了,B没有加钱
- 隔离性(Isolation):在并发环境中,每一个事务都是相互独立的。事务与事务之间互相不影响,都有自己独立、完整的数据空间。隔离性分为四个等级
- 持久性(Duration):事务一旦提交,数据库中的数据被永久保存
3. 事务在执行过程中可能出现的并发问题
- 脏读:读取到了(另一个事务中)没有提交的数据
- 不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交(考虑在数据内容上的变化)
- 幻读:重复查询的过程中,数据发生了量的变化insert/delete(考虑在量上的变化)
4. 事务隔离级别
如下表所示,隔离级别从低到高,隔离级别越高,安全性越高,并发性越低。顺序读不支持并发
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ_UNCOMMITTED) | 允许 | 允许 | 允许 |
读已提交(READ_COMMITTED) | 禁止 | 允许 | 允许 |
可重复读(REPEATABLE_READ) | 禁止 | 禁止 | 可能会 |
顺序读(SERIALIZABLE) | 禁止 | 禁止 | 禁止 |
二、对于事务各个隔离级别的举例说明
1. 查看会话中的事务隔离级别(用于mysql8以后的版本)
mysql默认的事务隔离级别是REPEATABLE_READ
select @@tx_isolation;
2. 设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
3.1 读未提交(READ_UNCOMMITTED)
并发运行事务A和事务B时会出现的场景:
时间 | 事务A(存款) | 事务B(取款) |
---|---|---|
T1 | start transaction | — |
T2 | — | start transaction |
T3 | — | 查询余额(1000元) |
T4 | — | 取出1000元(余额0元) |
T5 | 查询余额(0元) | — |
T6 | — | rollback |
T7 | 存入500元(人以为余额是500元,执行查询就发现变成了1500元) | — |
T8 | commit(显示余额1500元) | — |
T5时间点,事务A此时查询的余额为0,这个数据就是脏数据,他是事务B造成的,很明显是事务没有进行隔离造成的。
3.2 读已提交(READ_COMMITTED)
读已提交:是不同的事务执行的时候只能获取到已经提交的数据。 这样就不会出现上面的脏读的情况了。
但是在同一个事务中执行同一个读取,结果不一致。
读已提交解决了脏读问题,无法解决可重复读的问题
时间 | 事务A(存款) | 事务B(取款) |
---|---|---|
T1 | start transaction | — |
T2 | — | start transaction |
T3 | — | 查询余额(1000元) |
T4 | 查询余额(1000元) | — |
T5 | — | 取出1000元(余额0元) |
T6 | 查询余额(1000元) | — |
T7 | — | commit |
T8 | 查询余额(0元) | — |
T9 | commit | — |
3.3 可重复读(REPEATABLE_READ)
接下来请欣赏,诡异的更新事件:
时间 | 事务A | 事务B |
---|---|---|
T1 | start transaction | — |
T2 | 查询所有数据 | start transaction |
T3 | — | 插入(删除)一条数据 |
T4 | 查询所有数据(事务B未提交前看不出异常,在事务B中查询有变化) | commit |
T5 | 进行范围修改(发现影响的行数不合理) | — |
T6 | 查询所有数据(发现多出了一行) | — |
T7 | commit | — |
备注:如果使用REPEATABLE_READ的机制,在3.2中事务A的T8的查询结果,将会是1000元。但在完成了T9的提交以后,如果有T10,再进行查询就会查询到0元的结果。