【MySQL 笔记】事务(Transaction)详解
事务是 MySQL 中保证数据一致性和可靠性的最核心机制,尤其在涉及多条 SQL 语句需要“要么全成功、要么全失败”的场景(如转账、订单创建扣库存、下单减积分等)时,几乎是必须使用的。
一、事务的四大核心特性(ACID)
| 特性 | 英文 | 通俗解释 | MySQL 中的体现方式 |
|---|---|---|---|
| 原子性 | Atomicity | 要么全做,要么全不做 | undo log + MVCC + 回滚 |
| 一致性 | Consistency | 事务前后,数据库从一个一致状态到另一个一致状态 | 约束(主键、外键、CHECK)+ 业务逻辑 + 隔离级别 |
| 隔离性 | Isolation | 不同事务之间互不干扰 | 锁 + MVCC(多版本并发控制) |
| 持久性 | Durability | 事务一旦提交,数据永久保存 | redo log + 双写缓冲 + binlog(取决于刷盘策略) |
一句话总结:
ACID 让你的转账操作不会出现“钱扣了但没到账”或“别人看到中间状态”的灾难。
二、MySQL 事务基本语法
-- 方式一:显式事务(最常用、最清晰)
START TRANSACTION; -- 或 BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 提交(成功)
-- ROLLBACK; -- 回滚(失败或手动取消)
-- 方式二:自动提交(默认行为)
SET autocommit = 1; -- 默认就是 1
UPDATE ...; -- 每条语句立即提交
常用组合写法(生产推荐)
START TRANSACTION;
-- 核心业务逻辑
UPDATE account_a SET balance = balance - 500 WHERE id = 1001 AND balance >= 500;
-- 检查是否扣款成功(影响行数)
SET @affected = ROW_COUNT();
IF @affected = 0 THEN
ROLLBACK;
SELECT '余额不足' AS msg;
ELSE
UPDATE account_b SET balance = balance + 500 WHERE id = 1002;
COMMIT;
SELECT '转账成功' AS msg;
END IF;
三、MySQL 支持的事务隔离级别(最重要!)
MySQL InnoDB 默认隔离级别:REPEATABLE READ(可重复读)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | MySQL 默认 | 备注 / 实际表现(InnoDB) |
|---|---|---|---|---|---|
| READ UNCOMMITTED | 有 | 有 | 有 | 否 | 基本没人用(能读到未提交的数据) |
| READ COMMITTED | 无 | 有 | 有 | Oracle 默认 | 每次 SELECT 都读最新提交版本(电商常见) |
| REPEATABLE READ | 无 | 无 | 有(但 InnoDB 通过间隙锁极大减少) | MySQL 默认 | 同一事务内多次读相同数据结果一致 |
| SERIALIZABLE | 无 | 无 | 无 | 否 | 最高隔离,性能最差(几乎所有读写都加锁) |
InnoDB 在 REPEATABLE READ 下的特殊行为(非常重要):
- 通过 MVCC + 间隙锁(Gap Lock) + Next-Key Lock,在大多数场景下解决了幻读
- 但严格意义上仍然存在幻读可能性(只在特定加锁读 + 插入场景)
- 实际生产中,REPEATABLE READ 已经足够安全,性能也最好
四、事务常见使用场景 & 推荐隔离级别
| 业务场景 | 推荐隔离级别 | 为什么 | 是否需要显式事务 |
|---|---|---|---|
| 银行转账、支付扣款 | REPEATABLE READ | 防止脏读 + 不可重复读,间隙锁防超卖 | 必须 |
| 电商下单减库存 | REPEATABLE READ | 防止超卖(结合 SELECT … FOR UPDATE) | 必须 |
| 普通查询报表 | READ COMMITTED | 追求最新数据,接受不可重复读 | 可选 |
| 高并发日志写入 | READ COMMITTED | 性能优先,不关心幻读 | 通常不需要 |
| 数据一致性要求极高 | SERIALIZABLE | 几乎杜绝并发问题(但并发性能极差) | 必须 |
五、事务相关重要系统变量 & 查看方式
-- 查看当前会话隔离级别
SELECT @@transaction_isolation; -- 或 @@tx_isolation(旧版)
-- 查看全局默认隔离级别
SELECT @@global.transaction_isolation;
-- 修改当前会话隔离级别(只影响当前连接)
SET SESSION transaction_isolation = 'READ-COMMITTED';
-- 修改全局默认(重启后失效,需写配置文件)
SET GLOBAL transaction_isolation = 'READ-COMMITTED';
六、生产中事务常见坑 & 最佳实践(2026 年视角)
- 长事务 是最大杀手(锁住资源很久)
- 避免在事务里做大批量操作、循环查询
- 事务尽量控制在 100ms~几秒内
- 忘记 COMMIT / ROLLBACK
- 连接池环境下会自动回滚,但显式写更安全
- 在事务里混用 DDL
- DDL(如 ALTER TABLE)会隐式提交当前事务
- SELECT 不加锁就认为安全
- 普通 SELECT 不加锁 → 快照读
- SELECT … FOR UPDATE / LOCK IN SHARE MODE → 当前读(加锁)
- 推荐写法模板(转账/扣减库存)
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE; -- 行锁
-- 业务判断
UPDATE accounts SET balance = balance - 100 WHERE id = 1 AND balance >= 100;
IF ROW_COUNT() = 0 THEN
ROLLBACK;
ELSE
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
END IF;
下一期预告建议:MySQL 锁机制详解(行锁、表锁、间隙锁、Next-Key Lock、MVCC)
有哪种事务场景你项目里最常遇到(转账、秒杀、下单、批量更新等),或者对隔离级别、幻读还有疑问,欢迎留言,我们可以针对性深入~
祝大家事务写得稳,数据不丢不乱!🔄