
本系列可作为数据库学习系列的笔记文中提到的一些练习的代码小编会将代码复制下来大家复制下来就可以练习了方便大家学习。点赞关注不迷路您的点赞、关注和收藏是对小编最大的支持和鼓励系列文章目录JAVA初阶---------已更完JAVA数据结构---------已更完数据库---Day 1 数据库基础数据库---Day2 数据库操作数据库---Day3 数据类型数据库---Day4 数据表的操作数据库---Day5 数据表的增删改查数据库---Day6 数据库约束数据库---Day7 数据表设计数据库---Day8 多表联合查询数据库---Day9 视图数据库---Day10 索引数据库---Day11 事务数据库---Day12 JDBC目录目录系列文章目录目录前言一、什么是事务二、为什么需要事务三、事务的 ACID 特性四、Atomicity原子性五、Consistency一致性六、Isolation隔离性七、Durability持久性八、MySQL 中支持事务的存储引擎九、事务的基本语法十、事务提交示例十一、事务回滚示例十二、COMMIT 和 ROLLBACK 的区别十三、保存点 SAVEPOINT十四、自动提交事务十五、设置自动提交和手动提交十六、事务的隔离性十七、MySQL 的四种隔离级别十八、查看当前隔离级别十九、设置事务隔离级别二十、READ UNCOMMITTED读未提交二十一、脏读的危害二十二、READ COMMITTED读已提交二十三、不可重复读二十四、不可重复读的危害二十五、REPEATABLE READ可重复读二十六、幻读二十七、不可重复读和幻读的区别二十八、InnoDB 如何解决幻读二十九、SERIALIZABLE串行化三十、四种隔离级别对比三十一、事务使用中的常见错误1. 忘记提交事务2. 忘记回滚事务3. 以为 COMMIT 后还能 ROLLBACK4. 混淆自动提交和手动事务5. 在不支持事务的表上使用事务三十二、事务在实际业务中的应用场景1. 银行转账2. 电商下单3. 库存系统4. 支付系统5. 积分系统三十三、事务和锁的关系三十四、事务的最佳实践1. 事务要尽量短2. 不要在事务中调用不稳定的外部接口3. 操作顺序要固定4. 选择合适的隔离级别5. 一定要处理异常三十五、完整事务练习脚本三十六、事务知识点总结三十七、如何记住事务总结前言小编作为新晋码农一枚会定期整理一些写的比较好的代码作为自己的学习笔记会试着做一下批注和补充如转载或者参考他人文献会标明出处非商用如有侵权会删改欢迎大家斧正和讨论一、什么是事务在学习 MySQL 的过程中事务是一个非常重要的知识点。很多初学者刚开始接触事务时可能会觉得事务只是几个简单的 SQL 语句例如START TRANSACTION; COMMIT; ROLLBACK;但实际上事务并不是简单的语法问题而是数据库保证数据正确性、一致性和安全性的核心机制。所谓事务就是把一组 SQL 操作打包成一个整体来执行。在这个整体中所有 SQL 要么全部执行成功要么全部执行失败。事务中的 SQL 可以是一条也可以是多条。只要这些操作在业务上属于同一个完整流程就可以被放入同一个事务中。最典型的例子就是银行转账。假设有一张账户表bank_accountCREATE TABLE bank_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, balance DECIMAL(10, 2) NOT NULL ); INSERT INTO bank_account(name, balance) VALUES(张三, 1000); INSERT INTO bank_account(name, balance) VALUES(李四, 1000);现在张三要给李四转账 100 元那么数据库中至少需要执行两条更新语句UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四;从表面上看这只是两个普通的UPDATE语句。但是从业务角度看这两个操作必须被看作一个整体。因为转账业务必须满足以下要求第一张三的钱减少 100 元李四的钱必须增加 100 元不能只执行其中一条。第二转账前后张三和李四的账户总金额不能变化。转账前两人的总额是1000 1000 2000转账后应该是900 1100 2000第三转账成功后的结果必须保存到数据库中不能因为程序结束或者数据库重启就丢失。第四在转账过程中不能被其他事务干扰否则可能出现余额计算错误。因此事务的作用就是保证这组 SQL 操作在执行过程中保持正确、完整、可靠。简单来说事务 一组 SQL 操作的执行单元。事务的目标 要么全部成功要么全部失败。事务解决的问题 防止数据只改一半、防止并发混乱、防止系统异常导致数据不一致。二、为什么需要事务如果没有事务数据库在处理复杂业务时会非常危险。仍然以转账为例。假设张三给李四转账 100 元程序执行过程如下UPDATE bank_account SET balance balance - 100 WHERE name 张三;这条 SQL 执行成功后张三的余额从 1000 变成了 900。但是接下来程序还没来得及执行UPDATE bank_account SET balance balance 100 WHERE name 李四;服务器突然宕机了或者网络中断了或者程序报错了。这时数据库中就会出现非常严重的问题张三的钱已经扣了但是李四的钱没有增加。也就是说数据库中的钱凭空少了 100 元。这种情况在实际业务中是绝对不能接受的。银行、电商、库存、订单、支付、优惠券、积分等系统都不能允许数据只改一半。事务就是为了解决这类问题而出现的。在使用事务之后转账过程可以这样写START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四; COMMIT;如果中间任何一步出现问题就执行ROLLBACK;这样数据库会恢复到事务开始之前的状态。也就是说事务给了我们一个“后悔”的机会。如果所有操作都成功就提交如果中间出现错误就回滚。这就是事务最核心的价值。三、事务的 ACID 特性事务之所以可靠是因为它具有四个核心特性也就是常说的 ACID。ACID 分别代表AAtomicity原子性 CConsistency一致性 IIsolation隔离性 DDurability持久性这四个特性是理解事务的关键。四、Atomicity原子性原子性指的是一个事务中的所有操作要么全部成功要么全部失败。事务不能只执行一半。在转账案例中UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四;这两条 SQL 必须作为一个整体执行。如果两条都成功事务提交。如果其中任何一条失败事务回滚。原子性解决的问题是防止业务执行一半。可以这样理解事务就像一个不可拆分的最小执行单位。虽然事务内部可能有多条 SQL但是从业务结果看它们必须像一条 SQL 一样要么整体成功要么整体失败。例如在电商下单系统中可能会涉及以下操作1. 创建订单 2. 扣减库存 3. 扣减用户余额 4. 增加商家收入 5. 写入支付记录这些操作也必须放入一个事务中。如果订单创建成功但是库存没有扣减就会出现超卖。如果库存扣减成功但是订单创建失败就会出现库存凭空减少。如果余额扣减成功但是支付记录没有生成用户就可能无法证明自己已经付款。所以原子性是事务最基础、最直观的特性。五、Consistency一致性一致性指的是事务执行前后数据库都必须处于合法、正确的状态。一致性不是说数据不发生变化而是说数据变化之后仍然符合业务规则和数据库约束。仍然以转账为例。转账前张三1000 李四1000 总额2000转账后张三900 李四1100 总额2000虽然每个人的余额变化了但是总金额没有变化所以数据仍然是一致的。如果转账后变成张三900 李四1000 总额1900那就是不一致。如果转账后变成张三900 李四1200 总额2100也是不一致。一致性要求事务不能破坏数据库的完整性。数据库中的完整性包括很多方面1. 主键不能重复 2. 外键关系不能被破坏 3. 字段类型必须正确 4. 字段精度必须符合要求 5. 余额不能无故减少或增加 6. 库存不能出现负数 7. 订单状态不能乱跳例如订单状态一般会有这样的流程待支付 - 已支付 - 已发货 - 已完成不能出现待支付 - 已完成也不能出现已取消 - 已发货如果事务执行后破坏了这些业务规则那么即使 SQL 执行成功也不能说事务是正确的。因此一致性强调的是业务结果的正确性。六、Isolation隔离性隔离性指的是多个事务并发执行时它们之间不能互相干扰。MySQL 是一个多用户数据库系统。多个客户端可以同时连接数据库同时执行 SQL。例如用户 A 正在给用户 B 转账 用户 C 正在查询用户 B 的余额 用户 D 正在给用户 B 付款 用户 E 正在修改用户 B 的账户信息如果这些操作同时发生就可能互相影响。如果没有隔离性可能出现以下问题1. 一个事务读到了另一个事务还没有提交的数据 2. 同一个事务中两次读取同一条数据结果不一样 3. 同一个事务中两次查询同一个范围结果数量不一样 4. 多个事务同时修改同一条数据导致数据覆盖隔离性就是用来控制这些并发问题的。不过隔离性越强并发性能通常越低。隔离性越弱并发性能通常越高但是数据安全问题也越多。所以数据库提供了不同的隔离级别让开发者根据业务场景进行选择。七、Durability持久性持久性指的是事务一旦提交对数据的修改就会永久保存。也就是说只要执行了COMMIT;数据就应该被保存下来。即使数据库重启、服务器宕机只要事务已经提交数据也不应该丢失。例如START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四; COMMIT;提交成功后张三余额变成 900李四余额变成 1100。之后再次查询SELECT * FROM bank_account;应该能看到修改后的结果。持久性强调的是提交之后的数据必须可靠保存。八、MySQL 中支持事务的存储引擎在 MySQL 中并不是所有存储引擎都支持事务。常见的 MySQL 存储引擎包括InnoDB MyISAM MEMORY CSV ARCHIVE其中InnoDB 是 MySQL 中最常用的事务型存储引擎。如果要使用事务表的存储引擎应该是 InnoDB。可以通过以下语句查看 MySQL 支持的存储引擎SHOW ENGINES;在结果中如果某个存储引擎的Transactions列为YES说明它支持事务。InnoDB 通常会显示Transactions: YES而 MyISAM 通常不支持事务。因此在实际开发中如果你需要事务、行级锁、外键等能力应该优先使用 InnoDB。九、事务的基本语法MySQL 中事务的基本控制语句主要有三个START TRANSACTION; COMMIT; ROLLBACK;也可以使用BEGIN;开启事务。完整语法如下START TRANSACTION; -- 执行业务 SQL COMMIT;如果出现问题START TRANSACTION; -- 执行业务 SQL ROLLBACK;其中START TRANSACTION开启一个新的事务 BEGIN也可以开启一个新的事务 COMMIT提交事务让修改永久生效 ROLLBACK回滚事务取消当前事务中的修改需要注意的是无论执行COMMIT还是ROLLBACK当前事务都会结束。十、事务提交示例我们先准备账户表DROP TABLE IF EXISTS bank_account; CREATE TABLE bank_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, balance DECIMAL(10, 2) NOT NULL ) ENGINEInnoDB; INSERT INTO bank_account(name, balance) VALUES(张三, 1000); INSERT INTO bank_account(name, balance) VALUES(李四, 1000);查询数据SELECT * FROM bank_account;结果------------------- | id | name | balance | ------------------- | 1 | 张三 | 1000.00 | | 2 | 李四 | 1000.00 | -------------------现在开启事务START TRANSACTION;执行转账UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四;查询当前事务中的数据SELECT * FROM bank_account;结果------------------- | id | name | balance | ------------------- | 1 | 张三 | 900.00 | | 2 | 李四 | 1100.00 | -------------------如果确认没有问题执行提交COMMIT;提交之后再次查询SELECT * FROM bank_account;结果仍然是张三900 李四1100这说明事务已经提交数据修改已经生效。十一、事务回滚示例如果我们不想让修改生效可以使用ROLLBACK。先开启事务START TRANSACTION;执行修改UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四;此时在当前事务中查询SELECT * FROM bank_account;可以看到张三800 李四1200但是如果执行ROLLBACK;事务会被回滚。再次查询SELECT * FROM bank_account;数据会恢复到事务开始之前的状态。这说明事务中的修改并没有真正生效。十二、COMMIT 和 ROLLBACK 的区别COMMIT和ROLLBACK是事务控制中最重要的两个命令。它们的区别如下命令含义执行后结果COMMIT提交事务修改永久生效ROLLBACK回滚事务修改被取消COMMIT 后事务结束不能再回滚ROLLBACK 后事务结束数据恢复到事务开始前需要特别注意已经提交的事务不能再回滚。例如START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; COMMIT; ROLLBACK;这里的ROLLBACK已经无法撤销前面的COMMIT。因为事务在COMMIT后已经结束数据已经持久化。十三、保存点 SAVEPOINT在复杂事务中有时候我们不想把整个事务全部回滚而是只想回滚到事务中的某一个位置。这时可以使用保存点。保存点的语法是SAVEPOINT 保存点名称;回滚到保存点ROLLBACK TO 保存点名称;保存点的作用是在事务执行过程中标记一个位置后续如果出现问题可以回到这个位置而不是回到事务最开始。例如START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四; SAVEPOINT savepoint1; INSERT INTO bank_account(name, balance) VALUES(王五, 1000); ROLLBACK TO savepoint1; COMMIT;这个事务中1. 张三减少 100 2. 李四增加 100 3. 设置保存点 savepoint1 4. 插入王五 5. 回滚到 savepoint1最终结果是张三和李四的余额修改保留 王五这条记录被回滚保存点适合用在复杂业务中。例如1. 创建订单 2. 扣减库存 3. 使用优惠券 4. 使用积分 5. 写入日志如果写入日志失败可能不需要回滚整个订单只需要回滚日志部分。不过在真实业务中是否使用保存点需要结合业务逻辑谨慎设计。十四、自动提交事务MySQL 默认是自动提交事务的。也就是说当我们执行一条INSERT、UPDATE或DELETE语句时MySQL 会自动开启事务并在语句执行完成后自动提交。例如UPDATE bank_account SET balance balance - 100 WHERE name 张三;如果没有显式开启事务这条 SQL 执行成功后会自动提交。可以通过以下语句查看当前是否开启自动提交SHOW VARIABLES LIKE autocommit;如果结果是autocommit ON说明自动提交开启。如果结果是autocommit OFF说明自动提交关闭。十五、设置自动提交和手动提交开启自动提交SET AUTOCOMMIT 1;或者SET AUTOCOMMIT ON;关闭自动提交SET AUTOCOMMIT 0;或者SET AUTOCOMMIT OFF;当自动提交关闭后执行修改语句不会自动提交需要手动执行COMMIT;或者ROLLBACK;需要注意只要使用了START TRANSACTION;或者BEGIN;显式开启事务那么就必须通过COMMIT提交或者通过ROLLBACK回滚。此时是否开启autocommit并不影响当前显式事务。十六、事务的隔离性事务的隔离性主要解决并发问题。在实际开发中数据库通常不是只有一个用户访问而是有大量用户同时访问。例如用户 A 查询商品库存 用户 B 正在下单扣库存 用户 C 正在修改商品价格 用户 D 正在取消订单恢复库存这些操作可能同时发生。如果没有隔离性就会出现数据混乱。例如用户 A 正在查询商品库存时用户 B 修改了库存但还没有提交。如果用户 A 读到了这个未提交的数据那么用户 A 看到的结果可能是不可靠的。为了控制事务之间的影响数据库提供了事务隔离级别。十七、MySQL 的四种隔离级别MySQL InnoDB 中常见的事务隔离级别有四种READ UNCOMMITTED读未提交 READ COMMITTED读已提交 REPEATABLE READ可重复读 SERIALIZABLE串行化从上到下隔离级别越来越高数据安全性越来越强但并发性能通常越来越低。可以简单理解为READ UNCOMMITTED性能最好安全性最差 READ COMMITTED解决脏读但可能不可重复读 REPEATABLE READMySQL 默认级别解决不可重复读 SERIALIZABLE最安全但并发性能最低十八、查看当前隔离级别查看全局隔离级别SELECT GLOBAL.transaction_isolation;查看当前会话隔离级别SELECT SESSION.transaction_isolation;在 MySQL InnoDB 中默认隔离级别通常是REPEATABLE-READ也就是可重复读。十九、设置事务隔离级别设置全局隔离级别SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;设置当前会话隔离级别SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;如果不指定GLOBAL或SESSION只针对下一个事务生效SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;也可以使用变量方式设置SET GLOBAL transaction_isolation SERIALIZABLE;或者SET SESSION transaction_isolation REPEATABLE-READ;需要注意如果隔离级别中间有空格例如REPEATABLE READ在变量方式中通常要写成REPEATABLE-READ二十、READ UNCOMMITTED读未提交READ UNCOMMITTED是最低的隔离级别。在这个隔离级别下一个事务可以读取另一个事务还没有提交的数据。这种现象叫做脏读。例如事务 A 执行START TRANSACTION; INSERT INTO bank_account(name, balance) VALUES(王五, 2000);但是事务 A 还没有提交。此时事务 B 执行START TRANSACTION; SELECT * FROM bank_account;如果事务 B 能看到王五这条记录就说明事务 B 读到了事务 A 尚未提交的数据。如果之后事务 A 执行ROLLBACK;王五这条记录会被撤销。那么事务 B 之前读到的数据就是无效数据。这就是脏读。脏读非常危险因为它读取的是“不确定是否会真正存在”的数据。在实际业务中通常不建议使用READ UNCOMMITTED。二十一、脏读的危害脏读的危害非常大。例如在银行系统中事务 A 正在给用户充值 1000 元但还没有提交。事务 B 查询用户余额时读到了这 1000 元。然后事务 B 根据这个余额允许用户消费。但是事务 A 最后回滚了。这时用户实际上并没有充值成功却已经消费了这笔钱。这就是严重的数据安全问题。在订单系统中脏读也可能导致1. 读取到未提交订单 2. 读取到未确认库存 3. 读取到未完成支付 4. 根据错误数据生成后续业务所以脏读一般必须避免。二十二、READ COMMITTED读已提交READ COMMITTED比READ UNCOMMITTED更安全。在这个隔离级别下一个事务只能读取其他事务已经提交的数据。也就是说如果事务 A 修改了数据但没有提交事务 B 是读不到的。这样就可以解决脏读问题。例如事务 ASTART TRANSACTION; UPDATE bank_account SET balance 2000 WHERE name 王五;事务 A 没有提交。事务 BSTART TRANSACTION; SELECT * FROM bank_account WHERE name 王五;在READ COMMITTED下事务 B 读不到事务 A 未提交的修改。只有当事务 A 执行COMMIT;事务 B 后续查询才可能看到修改后的数据。但是READ COMMITTED仍然存在一个问题不可重复读。二十三、不可重复读不可重复读指的是在同一个事务中两次读取同一条数据结果不一致。例如事务 B 第一次查询王五余额START TRANSACTION; SELECT * FROM bank_account WHERE name 王五;结果王五2000此时事务 A 修改王五余额并提交START TRANSACTION; UPDATE bank_account SET balance 1000 WHERE name 王五; COMMIT;事务 B 再次查询SELECT * FROM bank_account WHERE name 王五;结果变成王五1000事务 B 还没有结束但两次读取同一条数据结果不同。这就是不可重复读。不可重复读的核心是同一个事务中同一条记录被读取多次结果发生变化。二十四、不可重复读的危害不可重复读会导致事务内部逻辑判断不稳定。例如一个事务先查询用户余额是否足够第一次查询余额 2000程序判断用户可以支付。但是在同一个事务后续处理中又查询了一次余额第二次查询余额 1000结果发现余额不足。这样业务逻辑就可能混乱。再比如报表统计一个事务开始生成报表第一次查询订单金额是 10000 元过一会儿第二次查询变成 12000 元。如果报表生成过程需要多次查询就可能出现前后数据不一致的问题。因此如果业务要求同一个事务中多次读取结果一致就不能只使用READ COMMITTED。二十五、REPEATABLE READ可重复读REPEATABLE READ可以解决不可重复读问题。在这个隔离级别下同一个事务中多次读取同一条数据结果应该保持一致。MySQL InnoDB 默认隔离级别就是REPEATABLE READ。例如事务 BSTART TRANSACTION; SELECT * FROM bank_account WHERE name 王五;第一次查询结果王五2000此时事务 A 修改王五余额并提交START TRANSACTION; UPDATE bank_account SET balance 1000 WHERE name 王五; COMMIT;事务 B 再次查询SELECT * FROM bank_account WHERE name 王五;在可重复读隔离级别下事务 B 仍然看到王五2000这样就保证了同一个事务内部读取结果的一致性。二十六、幻读虽然REPEATABLE READ解决了不可重复读但在理论上还可能出现幻读。幻读指的是在同一个事务中两次按照同一个条件查询一个范围第二次查询出现了第一次没有出现的新记录。例如事务 A 第一次查询SELECT * FROM bank_account WHERE balance 1000;结果查到 2 条记录。此时事务 B 插入一条新记录INSERT INTO bank_account(name, balance) VALUES(赵六, 1500); COMMIT;事务 A 再次执行SELECT * FROM bank_account WHERE balance 1000;如果这时查到 3 条记录就好像多了一条“幻影”一样。这就是幻读。幻读的核心是同一个事务中同一个范围查询两次结果集数量不一致。二十七、不可重复读和幻读的区别不可重复读和幻读很容易混淆。它们的区别是问题关注点示例不可重复读同一条记录内容变化第一次读余额 2000第二次读余额 1000幻读查询结果集数量变化第一次查 2 条第二次查 3 条不可重复读重点是 UPDATE其他事务修改了已存在记录幻读重点是 INSERT其他事务插入了符合条件的新记录简单记忆不可重复读同一行数据变了。幻读多出来或少了一行数据。二十八、InnoDB 如何解决幻读在 MySQL InnoDB 中REPEATABLE READ默认使用了 Next-Key Lock也就是临键锁。Next-Key Lock 可以理解为记录锁 间隙锁记录锁锁住已经存在的记录。间隙锁锁住记录之间的间隙。这样可以防止其他事务在查询范围内插入新记录从而解决大部分幻读问题。例如SELECT * FROM bank_account WHERE balance BETWEEN 1000 AND 2000 FOR UPDATE;InnoDB 不仅会锁住已有的符合条件的记录还可能锁住这些记录之间的范围防止其他事务插入新的符合条件的数据。所以在 MySQL InnoDB 中默认的REPEATABLE READ已经可以解决大部分幻读问题。二十九、SERIALIZABLE串行化SERIALIZABLE是最高的隔离级别。在这个隔离级别下事务会尽可能按照串行方式执行。也就是说多个事务之间像排队一样一个一个执行。这样可以最大程度保证数据安全避免脏读、不可重复读和幻读。但是缺点也非常明显并发性能会变差。因为事务之间互相等待数据库吞吐量会下降。例如事务 A 正在查询某些数据时事务 B 可能无法修改这些数据必须等待事务 A 结束。因此SERIALIZABLE适合数据一致性要求极高、并发量较低的场景。普通业务系统通常不会默认使用它。三十、四种隔离级别对比隔离级别脏读不可重复读幻读并发性能安全性READ UNCOMMITTED可能出现可能出现可能出现最高最低READ COMMITTED不会出现可能出现可能出现较高较低REPEATABLE READ不会出现不会出现InnoDB 大部分可避免较好较高SERIALIZABLE不会出现不会出现不会出现最低最高在实际开发中MySQL 默认的REPEATABLE READ是一个比较平衡的选择。它兼顾了并发性能和数据安全。三十一、事务使用中的常见错误1. 忘记提交事务如果开启事务后忘记执行COMMIT;修改不会真正持久化。在某些情况下还可能长时间占用锁影响其他事务执行。2. 忘记回滚事务如果事务执行过程中发生异常但程序没有执行ROLLBACK;可能导致连接一直处于事务状态影响后续操作。3. 以为 COMMIT 后还能 ROLLBACK这是初学者常见误区。一旦执行COMMIT事务就结束了修改已经生效不能再通过ROLLBACK撤销。4. 混淆自动提交和手动事务即使autocommit ON只要显式执行了START TRANSACTION;也必须手动提交或回滚。5. 在不支持事务的表上使用事务如果表使用的是不支持事务的存储引擎例如 MyISAM那么即使写了事务语句也无法达到真正的事务效果。三十二、事务在实际业务中的应用场景事务广泛应用在各种需要保证数据一致性的业务中。1. 银行转账扣减转出账户余额 增加转入账户余额 写入转账流水这些操作必须全部成功。2. 电商下单创建订单 扣减库存 锁定优惠券 扣减余额 写入支付记录任何一步失败都可能需要整体回滚。3. 库存系统扣减库存 增加销售记录 生成出库单如果库存扣了但出库单没生成系统就会不一致。4. 支付系统扣用户余额 增加商家余额 生成支付流水 更新订单状态支付系统对事务要求非常高。5. 积分系统用户消费 增加积分 写入积分流水如果积分增加了但流水没有记录后续就无法追踪。三十三、事务和锁的关系事务和锁经常一起出现。事务解决的是一组 SQL 操作的一致性问题。锁解决的是并发访问时的数据竞争问题。在 InnoDB 中事务执行更新操作时通常会加锁。例如UPDATE bank_account SET balance balance - 100 WHERE name 张三;这条语句会对符合条件的记录加锁防止其他事务同时修改同一条数据。如果没有锁两个事务同时修改余额可能会发生覆盖。例如事务 A 读取余额 1000准备减 100 事务 B 读取余额 1000准备减 200 事务 A 写入 900 事务 B 写入 800最终余额可能是 800而不是正确的 700。所以事务和锁是数据库并发控制中密切相关的两个概念。三十四、事务的最佳实践1. 事务要尽量短事务开启后不要执行太多无关操作。不推荐这样START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; -- 中间进行大量业务计算 -- 调用远程接口 -- 等待用户输入 UPDATE account SET balance balance 100 WHERE id 2; COMMIT;事务时间越长占用锁的时间越长并发性能越差。2. 不要在事务中调用不稳定的外部接口例如调用支付接口 调用短信接口 调用第三方 API这些操作可能很慢也可能失败。如果放在数据库事务中会导致事务长时间不结束。3. 操作顺序要固定如果多个事务都要操作多张表最好保持相同顺序。例如所有事务都按照先更新账户表 再写流水表 再更新订单表这样可以减少死锁概率。4. 选择合适的隔离级别不是隔离级别越高越好。隔离级别越高锁冲突可能越多并发性能可能越差。大多数 MySQL 业务使用默认的REPEATABLE READ即可。5. 一定要处理异常在应用程序中事务通常要配合异常处理。伪代码如下try { 开启事务; 执行业务 SQL; 提交事务; } catch (Exception e) { 回滚事务; }核心原则是成功就提交失败就回滚。三十五、完整事务练习脚本下面是一份适合初学者练习的完整 SQL 脚本。DROP TABLE IF EXISTS bank_account; CREATE TABLE bank_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, balance DECIMAL(10, 2) NOT NULL ) ENGINEInnoDB; INSERT INTO bank_account(name, balance) VALUES(张三, 1000); INSERT INTO bank_account(name, balance) VALUES(李四, 1000); SELECT * FROM bank_account; START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四; SELECT * FROM bank_account; ROLLBACK; SELECT * FROM bank_account; START TRANSACTION; UPDATE bank_account SET balance balance - 100 WHERE name 张三; UPDATE bank_account SET balance balance 100 WHERE name 李四; COMMIT; SELECT * FROM bank_account;通过这段脚本可以练习1. 创建支持事务的 InnoDB 表 2. 开启事务 3. 执行更新操作 4. 回滚事务 5. 提交事务 6. 观察提交和回滚的区别三十六、事务知识点总结事务是数据库中非常重要的机制它可以保证一组 SQL 操作作为一个整体执行。事务的核心思想是要么全部成功要么全部失败。事务具有四个 ACID 特性原子性事务中的操作不可分割 一致性事务执行前后数据必须正确 隔离性多个事务之间不能互相干扰 持久性事务提交后数据永久保存MySQL 中使用事务的基本语法是START TRANSACTION; COMMIT; ROLLBACK;也可以使用BEGIN;开启事务。保存点可以让事务回滚到中间某个位置SAVEPOINT savepoint1; ROLLBACK TO savepoint1;MySQL 默认开启自动提交SHOW VARIABLES LIKE autocommit;可以通过以下语句关闭自动提交SET AUTOCOMMIT OFF;MySQL InnoDB 支持四种事务隔离级别READ UNCOMMITTED读未提交 READ COMMITTED读已提交 REPEATABLE READ可重复读 SERIALIZABLE串行化不同隔离级别会产生不同并发问题READ UNCOMMITTED 可能出现脏读 READ COMMITTED 解决脏读但可能出现不可重复读 REPEATABLE READ 解决不可重复读InnoDB 中可解决大部分幻读 SERIALIZABLE 安全性最高但并发性能最低实际开发中MySQL 默认的REPEATABLE READ通常已经能满足大多数业务需求。三十七、如何记住事务可以用一句话记住事务事务就是把多条 SQL 绑在一起要么一起成功要么一起失败。可以用转账案例记住 ACID张三扣钱、李四加钱必须一起完成这叫原子性。 转账前后总金额不能变这叫一致性。 转账过程中不能被其他事务干扰这叫隔离性。 转账成功后结果不能丢失这叫持久性。可以用一句话记住隔离级别读未提交会脏读读已提交会不可重复读可重复读防止数据反复变化串行化最安全但最慢。可以用一句话区分不可重复读和幻读不可重复读是同一行数据变了幻读是查询结果多了或少了行。掌握这些内容之后就基本掌握了 MySQL 事务的核心知识。总结以上就是今天要讲的内容本文简单记录了数据库学习内容仅作为一份简单的笔记使用大家根据注释理解您的点赞关注收藏就是对小编最大的鼓励