【MySQL】第五节 - 事务实战详解:从基础到并发控制(附 Navicat 可运行实验脚本)

为什么你必须掌握 MySQL 事务?

在现代应用系统中,数据一致性是核心诉求。事务(Transaction) 是保证数据完整性的"黄金标准"。

无论是银行转账、库存扣减、订单创建,还是用户积分变动,事务机制都在默默守护数据安全

本文将带你从零开始,手把手用 Navicat 演示所有核心知识点 ,并附带 可直接复制粘贴运行的实验脚本,让你"边学边练",真正理解事务的本质。


一、什么是事务?------数据库中的"原子操作单元"

事务是一组数据库操作(SQL 语句)的集合,它们作为一个整体被执行,要么全部成功,要么全部失败回滚

1. 事务的四大特性:ACID

特性 说明 示例
A - 原子性(Atomicity) 事务中的所有操作必须全部完成,不能"部分提交" 张三转 1000 元给李四,若李四没到账,张三也要退回
C - 一致性(Consistency) 事务前后数据库必须保持一致性状态 总金额不变:张三减 1000,李四加 1000
I - 隔离性(Isolation) 多个事务并发执行时互不干扰 一个事务不可读到另一个事务未提交的数据
D - 持久性(Durability) 事务一旦提交,结果永久保存 即使系统崩溃,数据也不会丢失

理解 ACID,你才算真正掌握了"事务为什么重要"。


二、如何使用事务?------MySQL 两种模式

模式一:手动控制事务(推荐用于复杂逻辑)

sql 复制代码
-- 1. 关闭自动提交(设置为手动)
SET @@autocommit = 0;

-- 2. 开启事务
START TRANSACTION;

-- 3. 执行多个 SQL 操作
UPDATE account SET money = money - 1000 WHERE name = '张三';
UPDATE account SET money = money + 1000 WHERE name = '李四';

-- 4. 查看结果(此时仍可回滚)
SELECT * FROM account WHERE name IN ('张三', '李四');

-- 5. 成功:提交事务
COMMIT;
-- 6. 失败:回滚事务
-- ROLLBACK;

模式二:通过存储过程封装(适合复用)

sql 复制代码
-- 清空旧过程
DROP PROCEDURE IF EXISTS transfer;

-- 创建转账存储过程
DELIMITER //
CREATE PROCEDURE transfer()
BEGIN
    START TRANSACTION;

    -- 查询余额
    SELECT money INTO @balance FROM account WHERE name = '张三';

    -- 判断是否足够
    IF @balance >= 100 THEN
        UPDATE account SET money = money - 100 WHERE name = '张三';
        UPDATE account SET money = money + 100 WHERE name = '李四';
        COMMIT;
        SELECT '✅ 转账成功' AS result;
    ELSE
        ROLLBACK;
        SELECT '❌ 余额不足,已回滚' AS result;
    END IF;
END //
DELIMITER ;

-- 执行
CALL transfer();

小贴士:

  • SET @@autocommit = 1自动提交模式(默认)
  • SET @@autocommit = 0手动提交模式(必须手动 COMMIT/ROLLBACK)

三、事务的隔离级别:四层"防火墙"

并发事务可能导致数据不一致。MySQL 提供 4 种隔离级别 来控制这种干扰。

🔥 默认隔离级别是:REPEATABLE READ(MySQL 8.0)

隔离级别 是否脏读 是否不可重复读 是否幻读 适用场景
READ UNCOMMITTED ✅ 是 ✅ 是 ✅ 是 一般不用(风险太高)
READ COMMITTED ❌ 否 ✅ 是 ✅ 是 乐观锁场景、日志系统
REPEATABLE READ ❌ 否 ❌ 否 ⚠️ 一般(MVCC 优化) 推荐!默认级别
SERIALIZABLE ❌ 否 ❌ 否 ❌ 否 高度并发 + 高安全性(性能差)

查看当前隔离级别:

sql 复制代码
SELECT @@transaction_isolation;

设置隔离级别:

sql 复制代码
-- 临时设置(当前会话有效)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 或者
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

当然可以!以下是 对"四大并发事务问题"简明扼要、通俗易懂、适合发布在 CSDN 或技术博客的介绍,已为你优化为清晰、结构化、可读性强的表达:


四、四大并发事务问题简要介绍

在多用户并发操作数据库的场景下,多个事务可能同时读写同一数据,若没有合理控制,就会引发数据不一致问题。MySQL 事务隔离级别正是为了解决这些问题而设计的。

下面介绍四种最常见的并发异常,一文讲清 😎:


1. 脏读(Dirty Read)

"读到了还没提交的'假数据'"

  • 场景:事务 A 修改了数据但未提交,事务 B 就读取了 A 的修改。
  • 风险 :如果 A 后来 ROLLBACK,B 读到的数据就是"虚假"的,称为"脏数据"。
  • 解决方式 :使用 READ COMMITTED 或更高隔离级别。

示例:

张三转账 100 元给李四,但还没提交,李四的系统立刻查余额,发现到账了 ------ 结果一回滚,钱又没了!


2. 不可重复读(Non-Repeatable Read)

"同事务内,两次读同一数据,结果不一样"

  • 场景:事务 A 第一次读取某条记录,之后事务 B 修改了这条记录并提交,事务 A 再次读取,发现数据变了。
  • 注意:这是"数据被改了",不是新增或删除。
  • 解决方式 :使用 REPEATABLE READSERIALIZABLE

示例:

事务 A 查询"张三余额:1000",一会儿后再次查询,变成"800"------ 因为别人已转走 200。


3. 幻读(Phantom Read)

"同一事务中,读不到别人新增的行"

  • 场景 :事务 A 查询某类数据(如 WHERE age > 18),事务 B 插入一条符合条件的新数据并提交,事务 A 再次查询时发现"多出了一些行"。
  • 注意 :不是修改了老数据,而是新数据"凭空出现"
  • 解决方式 :使用 SERIALIZABLE 或 MySQL 的间隙锁(REPEATABLE READ 也部分防止)。

示例:

事务 A 查询"在职员工:5 人",事务 B 新增一名员工并提交,事务 A 再查,变成"6 人"。
小知识:在 MySQL 的 REPEATABLE READ 中,通常不会出现幻读(因为使用了"间隙锁"),但严格意义上仍有可能。


4. 补充:丢失更新(Lost Update)

虽不总列为"四大问题",但非常常见!

  • 场景:两个事务同时读取同一数据,各自修改后都提交,后提交的覆盖了先提交的。
  • 后果:一次更新被"覆盖",导致数据丢失。
  • 解决方式 :加锁(如 SELECT ... FOR UPDATE) + 事务控制。

总结对比表(建议收藏)

问题 描述 出现场景 隔离级别可避免
脏读 读到未提交数据 读未提交(RC) READ COMMITTED 以上
不可重复读 同事务内两次读不一致 修改数据 REPEATABLE READ 以上
幻读 查询结果"凭空多出" 新增/删除数据 SERIALIZABLE,MySQL RR 有优化
丢失更新 一次修改被覆盖 并发更新 ✅ 加锁 + 事务

一句话口诀记忆:

脏读 :读了没提交的"假数据"
不可重复读 :读了被改过的"变了"数据
幻读 :读到了"凭空出现"的新数据
丢失更新:改了被覆盖,白白努力


附:常见隔离级别应对效果

隔离级别 能防脏读? 能防不可重复读? 能防幻读?
READ UNCOMMITTED ❌ 否 ❌ 否 ❌ 否
READ COMMITTED ✅ 是 ❌ 否 ❌ 否
REPEATABLE READ(MySQL 默认) ✅ 是 ✅ 是 ✅(部分)
SERIALIZABLE ✅ 是 ✅ 是 ✅ 是

五、四大并发问题演示(Navicat 实验代码)

实验1:读未提交(Dirty Read)------脏读问题

sql 复制代码
-- 【窗口A】设置为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;

UPDATE account SET money = money - 200 WHERE name = '张三';
SELECT '窗口A:张三余额变为 800(未提交)' AS note;
SELECT * FROM account WHERE name = '张三';

-- 【窗口B】也设为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT '窗口B:可以读到未提交的数据' AS note;
SELECT * FROM account WHERE name = '张三';
-- ✅ 输出:800 → 即使A没提交,B也能读到!

实验2:读已提交(Read Committed)

sql 复制代码
-- 【窗口A】设置为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE account SET money = money - 100 WHERE name = '张三';

-- 【窗口B】读取(此时A未提交 → 读不到)
SELECT '窗口B:不能读到未提交的数据' AS note;
SELECT * FROM account WHERE name = '张三';
--  输出:1000 → 未看到A的变更!

-- 在A窗口提交后(COMMIT),B再查 → 就能看到新值

实验3:可重复读(Repeatable Read)------MVCC 机制

sql 复制代码
-- 【窗口A】设置为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

SELECT '窗口A:第一次读' AS note;
SELECT * FROM account WHERE name = '张三';

-- 等待【窗口B】提交修改(如:转100给李四)

-- 【窗口A】再次读
SELECT '窗口A:第二次读(仍为原值)' AS note;
SELECT * FROM account WHERE name = '张三';
--  输出:原值!即使别人提交了,我也读不到!

这就是 MySQL 的 MVCC(多版本并发控制):每个事务看到的是"快照",不会被其他事务干扰。


实验4:串行化(Serializable)------强制排队

sql 复制代码
-- 【窗口A】设置为串行化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;

SELECT '窗口A:读取张三余额' AS note;
SELECT * FROM account WHERE name = '张三';

-- 【窗口B】尝试读(会被阻塞,必须等A提交)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT '窗口B:等待(阻塞)' AS note;
SELECT * FROM account WHERE name = '张三';

-- 回到A窗口执行:COMMIT;
-- B窗口才恢复执行

串行化 = "一锤定音":事务间完全互斥,性能最低,但最安全。


五、关键工具 & 技巧汇总

技巧 说明
ROLLBACK 回滚事务,恢复到事务开始前状态
COMMIT 提交事务,永久保存修改
SELECT @@autocommit; 查看自动提交状态
SET SESSION TRANSACTION ISOLATION LEVEL ... 动态设置隔离级别
DELIMITER // ... // 定义存储过程边界
DROP PROCEDURE IF EXISTS ... 安全删除旧过程

如果你觉得这篇文章对你有帮助,请点赞 + 收藏 + 分享

欢迎在评论区留言:你遇到过的"事务坑"?

相关推荐
殷紫川2 小时前
避坑必看!MySQL 三大日志(redo/undo/binlog)底层原理全拆解,事务一致性再也不懵
mysql
AgCl233 小时前
MYSQL-5-DCL数据查询语言-3/16
数据库·mysql
IvorySQL3 小时前
PostgreSQL 技术日报 (4月7日)|内核开发新动态,多项功能优化落地
数据库·postgresql·开源
IvorySQL3 小时前
PostgreSQL 技术日报 (4月6日)|内核补丁与性能优化速递
数据库·postgresql·开源
IvorySQL3 小时前
PostgreSQL 技术日报 (4月5日)|六大核心补丁进展,生产环境必看
数据库·postgresql·开源
想唱rap3 小时前
线程的同步与互斥
linux·运维·服务器·数据库·mysql
IvorySQL3 小时前
走进高校,走近开源|IvorySQL 社区即将亮相应急管理大学开源学术报告会
数据库·postgresql·开源
殷紫川3 小时前
干掉 90% 慢 SQL!MySQL 全链路排查与优化方法论,从执行计划到表结构全拆解
mysql
splage3 小时前
Oracle分页sql
数据库·sql·oracle