MySql的事务机制
文章目录
前言
一、事务的核心特性:ACID
二、MySQL 事务的关键前提:存储引擎支持
三、MySQL 事务的基本操作
1. 核心事务控制命令
2. 实操示例(转账场景)
3. 自动提交机制(需注意的默认行为)
四、事务的隔离级别:解决并发冲突
1. 先了解:3 类并发问题
2. MySQL 的 4 个隔离级别
3. 隔离级别的相关操作
五、MySQL 事务的注意事项
总结
前言
MySQL的事务机制:搞定数据一致性的关键 做后端开发时,你一定遇到过这些糟心事:转账时“扣了钱没到账”、电商下单“超卖”、订单状态“部分更新”——其实这些问题,本质都是没用好MySQL事务。
事务是MySQL保障数据一致性的核心:它把多步SQL封装成“要么全成、要么全败”的单元,是金融、电商等场景的“数据安全阀”。但很多人只懂COMMIT/ROLLBACK,却搞不清ACID、隔离级别,还常踩“引擎选错事务失效”的坑。 我的这篇文章就帮你快速吃透事务:从ACID特性到隔离级别选择,再到实操避坑,用最干的内容解决实际问题,新手也能看懂。
MySQL 的事务(Transaction)是数据库中保证数据一致性和完整性的核心机制,它将一组 SQL 操作(如插入、更新、删除)封装成一个 “不可分割的逻辑单元”—— 这组操作要么全部执行成功(提交),要么全部执行失败(回滚),不会出现 “部分成功、部分失败” 的中间状态,尤其适用于转账、订单创建、库存扣减等对数据准确性要求极高的场景。
一、事务的核心特性:ACID
事务的可靠性由 ACID 四大特性 保障,这是理解事务的基础,缺一不可:
特性(英文) | 中文名称 | 核心含义(结合 MySQL 场景) | 示例(转账场景:A 给 B 转 100 元) |
---|---|---|---|
Atomicity | 原子性 | 事务中的所有操作 “要么全成,要么全败”,没有中间态;若某一步失败,整个事务会回滚到执行前的状态。 | 若 “扣减 A 的 100 元” 成功,但 “增加 B 的 100 元” 失败,事务回滚:A 的余额不变,B 的余额也不变。 |
Consistency | 一致性 | 事务执行前后,数据库的 “业务规则一致性” 不被破坏(如数据总量、约束条件不变)。 | 转账前 A+B 余额 = 2000 元,事务执行后 A+B 余额仍为 2000 元(不会出现 1900 或 2100 元的情况)。 |
Isolation | 隔离性 | 多个事务并发执行时,一个事务的操作不会被其他事务 “干扰”,每个事务都像在 “独立执行”。 | 若 A 同时给 B、C 转账,两个转账事务并发执行时,不会出现 “A 的余额被重复扣减” 或 “金额计算混乱”。 |
Durability | 持久性 | 事务一旦 “提交”(COMMIT),修改的数据会永久保存到磁盘,即使后续数据库崩溃,数据也不会丢失。 | A 给 B 转账的事务提交后,即使 MySQL 服务突然宕机,重启后 A 的余额仍会减少 100,B 的余额增加 100。 |
二、MySQL 事务的关键前提:存储引擎支持
并非所有 MySQL 存储引擎都支持事务!只有支持事务的存储引擎,才能使用 ACID 特性,核心区别如下:
- 支持事务的存储引擎:InnoDB(MySQL 5.5 及以后的默认存储引擎)、XtraDB 等;
- 不支持事务的存储引擎:MyISAM、MEMORY 等(这类引擎的操作是 “自动提交” 的,无法回滚,适合只读或低一致性要求的场景,如日志表)。
- 注意:若表的存储引擎是 MyISAM,即使手动写了事务语句(如 START TRANSACTION),MySQL 也会忽略事务逻辑,操作会直接生效且无法回滚。
三、MySQL 事务的基本操作
MySQL 中事务的操作通过 SQL 命令控制,核心命令分为 “事务控制” 和 “辅助操作” 两类:
1. 核心事务控制命令
命令 | 作用 |
---|---|
START TRANSACTION | 开启一个手动事务(也可用 BEGIN ,效果相同),之后的 SQL 会纳入事务管理。 |
COMMIT | 提交事务:将事务中所有 SQL 操作的修改 “永久写入磁盘”,事务结束。 |
ROLLBACK | 回滚事务:撤销事务中所有未提交的修改,恢复到事务开启前的状态,事务结束。 |
SAVEPOINT 保存点名称 | 在事务中创建 “保存点”,允许后续回滚到指定保存点(而非回滚整个事务)。 |
ROLLBACK TO 保存点名称 | 回滚到指定保存点,保存点之后的操作会被撤销,之前的操作仍处于 “未提交” 状态。 |
RELEASE SAVEPOINT 保存点名称 | 删除指定保存点。 |
BEGIN 或 START TRANSACTION 语句后面的 SQL 语句对数据库数据的更新操作都将记录在事务日志中,直至遇到 ROLLBACK 语句或 COMMIT语句。如果事务中某一操作失败且执行了 ROLLBACK 语句,那么在开启事务语句之后所有更新的数据都能回滚到事务开始前的状态。如果事务中的所有操作都全部正确完成,并且使用了 COMMIT 语句向数据库提交更新数据,则此时的数据又处在新的一致状态。
2. 实操示例(转账场景)
假设表 user_balance
结构如下:
id | user_id | balance |
---|---|---|
1 | A | 1000 |
2 | B | 1000 |
需求:A 给 B 转 100 元,用事务保证一致性:
-- 1. 开启事务
START TRANSACTION;-- 2. 执行核心操作(扣减 A 的余额,增加 B 的余额)
UPDATE user_balance SET balance = balance - 100 WHERE user_id = 'A';
UPDATE user_balance SET balance = balance + 100 WHERE user_id = 'B';-- 3. 验证操作结果(可选,用于手动确认数据是否正确)
SELECT * FROM user_balance WHERE user_id IN ('A', 'B');-- 4. 若验证正确,提交事务(数据永久生效);若错误,执行 ROLLBACK(撤销所有修改)
COMMIT;
-- 若第二步中某条 UPDATE 失败(如 B 的 user_id 不存在),则执行:
-- ROLLBACK;
3. 自动提交机制(需注意的默认行为)
MySQL 默认开启 自动提交(autocommit),即 “每一条 SQL 语句都是一个独立的事务”,执行后会自动 COMMIT
,无法回滚。若需手动控制事务,有两种方式:
- 临时关闭自动提交:
SET autocommit = 0;
(当前会话有效,关闭后需手动COMMIT
或ROLLBACK
); - 用
START TRANSACTION
开启事务:开启后,自动提交会被 “临时禁用”,直到COMMIT
或ROLLBACK
后恢复默认。
四、事务的隔离级别:解决并发冲突
当多个事务并发执行时,若隔离性控制不好,可能出现 3 类并发问题;MySQL 通过 “隔离级别” 来控制事务间的干扰程度,不同级别对应不同的并发安全性和性能。
1. 先了解:3 类并发问题
问题名称 | 含义(以 “事务 A 读取数据,事务 B 修改数据” 为例) |
---|---|
脏读(Dirty Read) | 事务 A 读取了事务 B “未提交” 的修改;若事务 B 后续回滚,事务 A 读取的就是 “无效数据”。 |
不可重复读(Non-repeatable Read) | 事务 A 两次读取同一数据,期间事务 B 对该数据做了 “提交修改”;导致事务 A 两次读取的结果不一致(读的是 “已提交的新数据”)。 |
幻读(Phantom Read) | 事务 A 按条件查询数据(如 “查询余额 < 500 的用户”),期间事务 B 新增 / 删除了符合该条件的记录;事务 A 再次查询时,结果行数变化。 |
脏读:
幻读:
2. MySQL 的 4 个隔离级别
MySQL 支持 4 种隔离级别(从低到高,安全性递增,性能递减),默认隔离级别是 Repeatable Read(可重复读)(InnoDB 引擎)。
隔离级别名称 | 英文缩写 | 能否避免脏读 | 能否避免不可重复读 | 能否避免幻读 | 核心特点(InnoDB 中) |
---|---|---|---|---|---|
读未提交 | RU | 不能 | 不能 | 不能 | 最低级别,允许读取未提交的数据,并发性能最高,但安全性极差,几乎不用。 |
读已提交 | RC | 能 | 不能 | 不能 | 只能读取已提交的数据,避免脏读;但同一事务内多次读同一数据可能不一致(不可重复读)。 |
可重复读(默认) | RR | 能 | 能 | 能 | 同一事务内多次读同一数据结果一致(避免不可重复读);InnoDB 还通过 “间隙锁” 避免幻读。 |
串行化 | Serializable | 能 | 能 | 能 | 最高级别,强制事务 “串行执行”(不并发),完全避免所有冲突,但性能极低,仅用于强一致性场景。 |
3. 隔离级别的相关操作
- 查看当前会话隔离级别:
SELECT @@session.tx_isolation;
(MySQL 8.0 用@@session.transaction_isolation
); - 查看全局隔离级别:
SELECT @@global.tx_isolation;
; - 修改当前会话隔离级别:
SET session tx_isolation = 'REPEATABLE-READ';
(需用英文引号,值为隔离级别名称); - 修改全局隔离级别:
SET global tx_isolation = 'READ-COMMITTED';
(需重启会话生效)。
五、MySQL 事务的注意事项
- 避免长事务:长事务(如执行时间超过几秒、包含大量 SQL)会占用数据库连接和锁资源,导致其他事务阻塞,影响并发性能;
- 事务粒度要合理:不要把无关的操作纳入同一个事务(如 “转账” 和 “日志记录” 可拆分),也不要把一个完整逻辑拆成多个事务;
- 依赖 InnoDB 引擎:若需事务支持,创建表时必须指定引擎为 InnoDB(
CREATE TABLE ... ENGINE=InnoDB;
); - 注意锁冲突:高并发场景下,事务若持有锁时间过长,可能导致死锁(两个事务互相等待对方释放锁);MySQL 会自动检测死锁并回滚其中一个事务,但需尽量通过优化 SQL 避免死锁。
总结
MySQL 事务是保障数据一致性的 “核心工具”,其核心是 ACID 特性,依赖 InnoDB 引擎实现;通过 START TRANSACTION
/COMMIT
/ROLLBACK
控制事务生命周期,通过 “隔离级别” 平衡并发安全性和性能;实际开发中,凡是涉及 “多步操作必须同时成功或失败” 的场景(如转账、订单),都必须用事务来控制。