目录
[三、MySQL 事务控制语句](#三、MySQL 事务控制语句)
[五、事务隔离级别(Isolation Levels)](#五、事务隔离级别(Isolation Levels))
前言
在现代数据库系统中,事务(Transaction) 是保障数据一致性和完整性的核心机制。无论是银行转账、订单支付,还是用户注册,背后都离不开事务的支撑。
想象这样一个场景:
张三向李四转账 100 元 。
这需要两步操作:
- 张三账户余额减 100;
- 李四账户余额加 100。
如果第一步成功,第二步因系统崩溃失败,就会导致资金凭空消失 !
而 MySQL 事务 正是解决此类问题的"安全锁"------它确保这两步要么全部成功,要么全部失败。
本文将深入讲解 MySQL 事务的原理、ACID 特性、控制语句、隔离级别及最佳实践。
一、事务是什么?
事务的定义
事务是一组 SQL 操作的逻辑单元,这些操作被视为一个整体:
- 全部执行成功 → 提交(COMMIT),永久生效;
- 任一操作失败 → 回滚(ROLLBACK),全部撤销。
重要前提
只有 InnoDB 存储引擎支持事务
MyISAM、MEMORY 等引擎不支持事务,操作会自动提交。
二、事务的四大特性(ACID)
| 特性 | 英文 | 说明 | 示例 |
|---|---|---|---|
| 原子性 | Atomicity | 操作不可分割,全做或全不做 | 转账两步必须同时成功或失败 |
| 一致性 | Consistency | 事务前后数据符合业务规则 | 转账后总金额不变(1000 → 900+100) |
| 隔离性 | Isolation | 并发事务互不干扰 | A 查余额时,B 的未提交转账不可见 |
| 持久性 | Durability | 提交后修改永久保存 | 即使断电,已提交的转账不会丢失 |
ACID 是事务的"黄金标准",缺一不可。
三、MySQL 事务控制语句
1. 开启事务
sql
-- 方法1:显式开启
START TRANSACTION;
-- 或
BEGIN;
-- 方法2:关闭自动提交
SET AUTOCOMMIT = 0; -- 之后每条 DML 都属于事务,需手动 COMMIT/ROLLBACK
📌 默认行为 :MySQL 默认
AUTOCOMMIT=1,即每条INSERT/UPDATE/DELETE自动提交!
2. 提交与回滚
sql
-- 提交:永久保存更改
COMMIT;
-- 回滚:撤销所有未提交的更改
ROLLBACK;
3. 保存点(Savepoint)------ 事务中的"检查点"
sql
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
SAVEPOINT before_transfer; -- 设置保存点
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
-- 若第二步出错,可回滚到保存点
ROLLBACK TO before_transfer;
-- 最终决定提交或完全回滚
COMMIT; -- 或 ROLLBACK;
优势:实现事务内的"部分回滚",提升灵活性。
四、实战案例:安全转账
sql
-- 开启事务
START TRANSACTION;
-- 扣款
UPDATE accounts
SET balance = balance - 100
WHERE user_id = 1 AND balance >= 100;
-- 检查扣款是否成功(防止余额不足)
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '余额不足';
END IF;
-- 入账
UPDATE accounts
SET balance = balance + 100
WHERE user_id = 2;
-- 提交
COMMIT;
关键点:
- 使用
ROW_COUNT()验证操作是否生效;- 通过
SIGNAL抛出错误终止流程(需在存储过程中使用)。
五、事务隔离级别(Isolation Levels)
并发事务可能引发三大问题:
| 问题 | 描述 | 隔离级别解决情况 |
|---|---|---|
| 脏读(Dirty Read) | 读到其他事务未提交的数据 | READ COMMITTED 及以上解决 |
| 不可重复读(Non-repeatable Read) | 同一事务内多次读取结果不同 | REPEATABLE READ 及以上解决 |
| 幻读(Phantom Read) | 同一查询返回新插入的行 | SERIALIZABLE 解决 |
MySQL 支持的四种隔离级别
| 隔离级别(Isolation Level) | 脏读(Dirty Read) | 不可重复读(Non-repeatable Read) | 幻读(Phantom Read) | 性能表现 |
|---|---|---|---|---|
| READ UNCOMMITTED | 允许 | 允许 | 允许 | 最高 |
| READ COMMITTED | 禁止 | 允许 | 允许 | 较高 |
| REPEATABLE READ(MySQL 默认) | 禁止 | 禁止 | InnoDB 通过 MVCC 解决 | 中等 |
| SERIALIZABLE | 禁止 | 禁止 | 禁止 | 最低(串行执行) |
查看与设置隔离级别
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局(影响新连接)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
InnoDB 的优化 :
在
REPEATABLE READ下,InnoDB 通过 MVCC(多版本并发控制) 和 Next-Key Lock 有效避免幻读,因此通常无需升级到SERIALIZABLE。
六、自动提交模式(AUTOCOMMIT)
| 模式 | 行为 | 适用场景 |
|---|---|---|
AUTOCOMMIT=1(默认) |
每条 DML 自动提交 | 简单脚本、交互式查询 |
AUTOCOMMIT=0 |
需手动 COMMIT/ROLLBACK | 应用程序、复杂业务逻辑 |
在应用程序中使用
php
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->beginTransaction(); // 相当于 SET AUTOCOMMIT=0 + START TRANSACTION
$pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE user_id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE user_id = 2");
$pdo->commit(); // 提交
} catch (Exception $e) {
$pdo->rollback(); // 回滚
throw $e;
}
?>
最佳实践 :应用程序中始终使用
beginTransaction()+commit()/rollback()。
七、事务使用注意事项
-
避免长事务
长时间未提交的事务会:
- 占用大量 undo 日志;
- 阻塞 purge 线程;
- 增加死锁概率。
-
合理选择隔离级别
- 大多数场景
REPEATABLE READ(默认)足够; - Oracle/SQL Server 默认是
READ COMMITTED,注意兼容性。
- 大多数场景
-
死锁处理
InnoDB 会自动检测死锁并回滚代价较小的事务。应用层应捕获
Deadlock found错误并重试。 -
不要在事务中处理用户交互
事务期间等待用户输入会导致锁长时间持有,影响并发性能。
八、总结:事务使用口诀
"InnoDB 才支持,ACID 要记牢;
BEGIN 开事务,COMMIT 才生效;
出错就 ROLLBACK,保存点更灵活;
默认可重复读,长事务要避免!"
掌握 MySQL 事务,不仅是写出正确代码的基础,更是构建高可靠、高并发系统的必备技能。合理运用事务,让你的数据在任何情况下都坚如磐石!