目录
小编之前介绍了微服务架构下的事务管理挑战及解决方案,而微服务中的分布式事务,本质是由多个数据库的 “本地事务” 协同组成的 —— 要搞懂复杂的分布式事务,得先把单机 MySQL 的事务基础打牢。今天小编就先来讲 MySQL 事务的核心特性与实践要点,我们来通过一个案例,更具象化地体现为什么需要事务。🌰🌰🌰🌰🌰🌰🌰🌰
事务案例
我们简单地创建了一个my_bank表,里面只有两个字段用户名·name,金额money。
从表中数据可以看出,小白和小新两个账户的余额总和为1001元
从小新的账户直接转账500元到小白的账户,使用UPDATE语句分别修改他们的账户,如下:
UPDATE my_bank SET money=money-500 WHERE NAME='小新';
UPDATE my_bank SET money=money+500 WHERE NAME='小白';
正常情况下,执行以上的转账操作后,余额总和应保持不变,仍为1001元。
但是,如果在这个过程中其中一个环节出现差错,例如在小新的账户减少500元之后,发生了服务器故障,小白的账户没有立即增加500元,此时,第三方读取到两个账户的余额总和变为500+1=501元,那账户总额就少了500元.....
什么是事务
MySQL为了解决此类问题,提供了事务。事务可以将一系列的数据操作捆绑成一个整体进行统一管理,如果某一事务执行成功,则在该事务中进行的所有数据更改均会提交,成为数据库中的永久组成部分。如果事务执行时遇到错误,则就必须取消或回滚。取消或回滚后,数据将全部恢复到操作前的状态,所有数据的更改均被清除。
MySQL执行事务的语法
默认设置下,每条SQL语句就是一个事务,即执行SQL语句后自动提交。为了达到将几个操作作为一个整体的目的,需使用BEGIN START TARNSACTION 开启一个事务
开启事务
这个语句显示地标记一个事务的起始点
BEGIN;
# 或者
START TRANSACTION;
提交事务
COMMIT表示提交事务,即提交事务的所有操作,将事务中的所有对数据库地更新都写到磁盘上地物理数据库中,因此也标志着一个事务地结束。一旦执行了该命令,将不能回滚事务。只有在所有修改都准备好提交给数据库时,才执行这一操作
COMMIT
回滚(撤销)事务
当事务执行过程中遇到错误时,使用ROLLBACK语句使事务回滚到起点或指定的保持点处。同时,系统将清除自事务起点或到某个保存点所做的所有的数据修改,并且释放由事务控制的资源。因此,这条语句也标志着事务的结束
ROLLBACK;
案例
在上一个栗子中,小新的账户余额已经减少到500元,如果再转出1000元,将会出现余额为负数,因此需要回滚到原始状态。
BEGIN;
UPDATE my_bank SET money=money-1000 WHERE NAME='小新';
ROLLBACK;
回滚后,查询账户信息,从结果可以看出,执行事务回滚后,账户数据恢复到初始状态,即该事务执行之前的状态
所以我们通过事务确保数据库操作的可靠性、一致性和完整性,避免因系统故障(如断电、网络中断)、操作错误或并发冲突导致的数据混乱。接下来我们来深入了解事务的特性
事务的特性和实现
任何一种数据库,都会拥有各种各样的日志,用来记录数据库的运行情况、日常操作、错误信息。例如,当用户root登录到MySQL服务器,就会在日志文件里记录该用户的登录时间、执行操作等。
- UNDO Log日志:记录事务执行前的数据,用于在事务发生异常时回滚数据,以此来确保数据的原子性;
- REDO Log日志:记录在事务执行中,每条对数据进行更新的操作,当事务提交时,该内容被刷新到磁盘。当MySQL意外宕机,InnoDB存储引擎会使用REDO Log恢复,以此来确保数据的持久性;
特性
事务具有 4 个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。这 4 个特性通常简称为 ACID。
原子性
原子性确保事务是一个不可分割的工作单位,事务中的所有操作要么全部成功提交,要么在发生错误全部回滚
实现原理:InnoDB通过undo log(回滚日志)实现原子性。当事务执行修改操作时,InnoDB会记录数据的原始状态到undo log。若事务需要回滚,系统可通过undo log恢复数据至修改前状态
持久性
持久性保证事务一旦提交,其修改会永久保存到数据库中,即使发生系统崩溃,断电等故障也不会丢失
实现原理:InnoDB通过redo log(重做日志)实现持久性。事务执行时,修改操作先写入redo log缓冲区,定期刷新到磁盘。即使数据页未写入磁盘,重启后可以通过redo log恢复已提交的事务
隔离性
隔离性控制多个并发事务之间的相互影响,防止因并发执行,引发的数据不一致。隔离级别越高,数据一致性越好,但并发性能越低
实现原理:隔离性是通过MVCC(多版本并发控制)或锁机制来保证
一致性
一致性指事务执行前后,数据库会从一个符合业务规则的 “合法状态”,转换到另一个同样符合业务规则的 “合法状态”(小编提醒:这里的 “合法” 是指数据满足业务逻辑约束,比如 “转账前后总资金守恒”“库存扣减后不能为负数”。并不是说事务执行前后数据完全相同,而是通过事务保障操作后的数据依然 “逻辑正确”)。
实现原理:一致性是事务追求的最终目标,由原子性、隔离性和持久性共同保障,同时依赖业务逻辑的正确设计(如代码中对数据合法性的校验)
并发事务出现的问题
MySQL允许多个客户端同时连接。所以,当多个事务同时处理,就会产生脏读、不可重复读、幻读等并发事务问题
脏读
一个事务读取到了另一个事务尚未提交的修改数据。因为它还没有提交事务有可能发生回滚或者执行失败,那就还是原数据,但是事务却读取了修改但未提交的数据
不可重复读
在一个事务内,多次读取同一数据集合时,同一行数据的内容发生了变化,通常是因为其他事务对该行数据进行了更新。导致第一次读取的数据和第二次读取的数据不一样
幻读
在一个事务内,多次读取同一个数据集合时,数据的行数发生了变化。一般是由于其他事务执行了插入或删除操作,导致结果集的记录数量改变
丢失修改
第一个事务修改数据后,第二个事务也修改了这个数据。导致第一个事务内的修改结果被丢失(也就是第二个事务修改的数据将第一个事务修改的数据覆盖了)
假设你的银行账户初始余额是 1000 元,现在有两个事务(可以理解为两个 “操作流程”)同时要修改你的余额:
事务 1 先修改了数据(把余额改成 1500),但事务 2 并不知道事务 1 做了修改(它读取的还是初始的 1000),最后事务 2 把余额改成 1300,导致事务 1 的修改结果被 “覆盖” 了(相当于白加了 500)。
为了解决以上这些问题,标准SQL定义了4类事务隔离级别
事务隔离级别
读未提交(READ UNCOMITTED,RU)
事务可读取其他未提交事务的修改,导致脏读、不可重复读、幻读
读已提交(READ COMMITTED,RC)
事务只能读取其他已提交事务的修改,可避免脏读,但仍可能出现不可重复读、幻读
可重复读(REPEATABLE READ,RR)
MySQL InnoDB默认隔离级别。事务正在读取数据,不允许其他事物进行修改操作,事务期间多次读取同一数据结果一致,可避免脏读、不可重复读,通过Next-Key Lock机制避免幻读
串行化(SERIALIZABLE)
当多个事务对同一条数据进行读写操作时,通过加锁的方式,强制事务串行执行。可避免所有并发问题,但并发性能极差
本节篇章的核心目标,是帮大家对 MySQL 事务建立初步认知:无论是解决数据混乱的 ACID 特性、实操性强的事务语法,还是并发场景下的脏读、幻读等问题,以及对应的隔离级别方案,都是咱们理解事务的基础。
需要特别注意的是,事务隔离级别的实现离不开‘锁机制’的支撑 —— 这也是理解 MySQL 并发控制的关键。下一篇章,小编就聚焦 MySQL 锁机制,带大家深入看看它是如何保障事务隔离性的~
MySQL内容丰富感谢大家耐心观看🐱
有问题欢迎留言!!!😗
肥嘟嘟左卫门就讲到这里啦,记得一键三连!!!😗