MYSQL——基础知识(SQL事务)
目录
前言
一、事务是什么?
二、事务的四大特性(ACID)
三、MySQL 事务控制语句
四、实战案例:安全转账
五、事务隔离级别(Isolation Levels)
六、自动提交模式(AUTOCOMMIT)
七、事务使用注意事项
八、总结:事务使用口诀
前言
在现代数据库系统中,事务(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. 开启事务
-- 方法1:显式开启 START TRANSACTION; -- 或 BEGIN; -- 方法2:关闭自动提交 SET AUTOCOMMIT = 0; -- 之后每条 DML 都属于事务,需手动 COMMIT/ROLLBACK📌默认行为:MySQL 默认
AUTOCOMMIT=1,即每条INSERT/UPDATE/DELETE自动提交!
2. 提交与回滚
-- 提交:永久保存更改 COMMIT; -- 回滚:撤销所有未提交的更改 ROLLBACK;3. 保存点(Savepoint)—— 事务中的“检查点”
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;优势:实现事务内的“部分回滚”,提升灵活性。
四、实战案例:安全转账
-- 开启事务 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 $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 事务,不仅是写出正确代码的基础,更是构建高可靠、高并发系统的必备技能。合理运用事务,让你的数据在任何情况下都坚如磐石!
