文章目录
一. 什么是事务?
1.1 为什么使用事务
数据库将数据有条理的存放在磁盘上, 支持数据的增删改查, 并在过程中保障数据的正确和可靠.
但想要做到这点并不容易, 举一个常见的例子就是银行转账: A账户给B账户转账2000(M1)当作生活费.
在交易的过程中, 就有几个问题出现了:
- 怎么保证在A账户减少1000的同时B账户增加1000.
- 如何保证在大量交易的同时, 数据仍是合法的, 钱没有凭空产生或消失.
- 如果交易完成时, 数据库恰好崩溃, 那么如何保证交易数据成功保存在数据库中?
- A账户如果同时在和C账户交易(M2), 如何让这两笔交易互不影响?
要保证交易的正常进行, 数据库就要解决以上的问题, 这就是事物诞生的原因.
1.2 事物的概念
数据库中: 事务(Transaction)是访问和更新数据库的程序执行单元, 事务中可能包含一个或多个sql语句, 这些语句要么都执行, 要么都不执行.
通俗来讲, 就是将多个操作打包在一起执行, 要么都执行, 要么都不执行(回滚).
二. 事务的四大特性(ACID)
2.1 原子性(Atomicity)
事务是数据库的逻辑工作单位, 事务中包括的操作要么全执行, 要么全不执行.
这里的"全不执行"不是真的指一个操作也不执行.
举个例子, 假如一个事务中有三个操作: 操作1, 2, 3.
先执行1, 再执行2, 最后执行3.
如果三个操作都执行成功了, 那么这个事务的所有操作都执行了.
如果操作1, 2执行成功了, 操作3失败了, 那么就自动地把前面已经执行成功的操作还原回最初没有执行的摸样. 这个操作, 就叫回滚
因此, "全不执行"只是看起来没执行.
回滚是如何实现的?
将数据库事务操作的中间操作通过特定的日志记录下来. 如果需要回滚, 就直接按照之前操作的逆操作来执行. 而且日志中的数据始终在硬盘上存在, 即使数据库的服务器崩溃, 再重启之后, 针对之前没有回滚玩的情况会继续处理.
利用这个特点, 我们就不用担心在交易过程中遇到程序崩溃或者是服务器崩溃, 而产生的A账户减少2000而B账户没收到钱 或者是 A账户钱没变但B账户凭空出现2000. A和B账户的金额变动要么同时成功, 要么同时失败.
这里要注意的是, MySQL
中, InnoDB
存储引擎支持回滚机制, 但是MyIsam
并不支持. 所以大家如果发现自己的MySQL
没有进行回滚操作, 那么你使用的存储引擎可能就是MyIsam
2.2 一致性(Consistency)
MySQL
中: 一致性是指事务执行结束后, 数据库的完整性约束没有被破坏, 事务执行的前后都是合法的数据状态。
数据库的完整性约束包括但不限于: 实体完整性(如行的主键存在且唯一), 列完整性(如字段的类型, 大小, 长度要符合要求) , 外键约束, 用户自定义完整性(如转账前后, 两个账户余额的和应该不变, 不能是A账户余额-2000, B账户余额+200 或者是 +3000 或者是其他)
如何保证数据的一致性?
一是通过约束; 二是通过回滚
2.3 持久性(Durability)
事务一旦提交,它对数据库的改变就应该是永久性的(写入了硬盘), 无论发生什么(崩溃或者出错).
2.4 隔离性(Isolation)
与其他特性侧重于研究事物本身不同, 隔离性研究的是不同事物之间的相互影响.
隔离性是指, **事物内部的操作与其他事务是隔离的, 并发执行的各个事物之间不能互相干扰. **隔离性越高, 并发性越低, 数据越可靠, 性能越低.
并发执行事务, 也就是同时执行多个事务, "同时"是指在宏观上是同时的, 但是微观上还是有先有后的.
通常使用锁机制来保证事物的隔离性.
若不保证事物的隔离性, 那么可能会出现数据的脏读, 不可重复读和幻读.
2.4.1 脏读, 不可重复读和幻读
(1) 脏读
事务A正在访问并修改数据, 事务B读到了A未提交的数据(脏数据), 这种现象就是脏读. 通过脏读读到脏数据是临时的, 不是最终准确的数据.
时间 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | 开始事务 |
T2 | 修改Tom的余额, 将余额由1000改成3000 | |
T3 | 读取Tom的余额, 结果为3000[脏读] | |
T4 | 修改Tom的余额, 将余额由1000改成5000 | |
T5 | 提交事务 | 提交事务 |
(2) 不可重复读
在事务A中先后读取同一个数据, 两次读取的数据不一样, 这种现象就叫不可重复读.
脏读和不可重复读的区别在于: 脏读读到的是其他事务未提交的的数据; 不可重复读读到的是其他事物已经提交的数据.
时间 | 事务A | 事务B |
---|---|---|
T1 | 开始事务 | |
T2 | 查询Tom的余额, 为1000 | |
T3 | 其他操作 | 开始事务 |
T4 | 修改Tom的余额, 1000->3000 | |
T5 | 提交事务 | |
T6 | 查询Tom的余额, 为3000 | |
T5 | 提交事务 |
(3) 幻读
事务A按照一定条件进行数据读取, 期间事务B插入或删除了相同搜索条件的新数据, 事务A再次按照原来的条件进行读取时, 发现了事务B新插入或删除的数据. 也就是说, 在事务A中按照某个条件先后两次查询数据库, 两次查询结果的条数不同, 这种现象叫做幻读.
时间 | 事务A | 事务B |
---|---|---|
T1 | 开始事务 | 开始事务 |
T2 | 查询条件1下的数据数, 100条 | |
T3 | 新增100条符合条件1的数据 | |
T4 | 提交事务 | |
T5 | 查询条件1下的数据数, 200条 |
不可重复读和幻读的区别:
- 不可重复读是前后读取到了不同的数据, 针对的是update操作
- 欢度是读取到了其他事物新增的数据, 针对的是insert和delete操作.
2.4.2 事务的隔离级别
SQL标准中定义了四种隔离级别, 并规定了每种隔离级别下, 上述几个问题是否存在.
隔离级别有4个, 由低到高分别为Read Uncommitted , Read Committed, Repeatable Read , Serializable.
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
Read Uncommitted(RU) | ✔️ | ✔️ | ✔️ |
Read Committed(RC) | ✖️ | ✔️ | ✔️ |
Repeatable Read(RR) | ✖️ | ✖️ | ✔️ |
Serializable | ✖️ | ✖️ | ✖️ |
-
DEFAULT
默认值, 表示使用底层数据库的默认隔离级别, MySQL默认为(RR)
-
Read Uncommitted(读未提交)
该级别表示事务A可以读取到事务B修改但是还没有提交的数据.
-
Read Committed(读提交)
该级别表示事务A只能读取事务B已经提交的数据.
-
Repeatable Read(可重复读)
该级别表示事务A在整个过程中可以多次读取某个数据, 并且每次返回的记录都相同, 即使在多次读取之间事务B修改了这个数据, 返回的还是修改前的数据.
但如果事务B没有修改现有数据, 而是添加了新的数据, 事务A是可以读取到新增数据的.
-
Serializable(串行化)
最高级别的隔离, 两个同时发生的事务100%隔离. 事务依次执行.
事务的隔离级别越高, 并发性越低, 数据可靠性越高, 效率越低.
三. 事务的使用
-
开启事务
start transaction;
-
输入多个sql语句
-
提交事务 / 手动触发回滚
提交事务:
commit;//全部执行
回滚:
rollback;//全不执行
⚠️一个事物必须要以commit;
或者rollback;
结尾, 否则接下来的操作都会被认为是事务的一部分.