事务
约 1340 字大约 4 分钟
2025-07-28
6.1 事务简介
事务是一组不可分割的操作集合,作为一个整体,要么全部成功,要么全部失败(原子性)。例如,银行转账操作包含转出账户扣款和转入账户收款两个步骤,这两步必须同时成功或失败以确保数据一致性。
正常情况下,转账流程分为:
- 查询张三账户余额。
- 张三账户余额减少 1000。
- 李四账户余额增加 1000。
若在第三步发生错误(如网络中断),张三账户已减少 1000 但李四账户未增加,会导致数据不一致。事务机制通过在业务逻辑执行前开启事务、执行完毕后提交事务来解决该问题。若执行中出错,则回滚事务以恢复原始状态。
注
MySQL 默认自动提交事务,即单条 DML 语句执行后立即隐式提交。
6.2 事务操作
6.2.1 未控制事务
正常情况测试 使用以下 SQL 模拟转账:
-- 1. 查询张三余额 SELECT * FROM account WHERE name = '张三'; -- 2. 张三余额减少 1000 UPDATE account SET money = money - 1000 WHERE name = '张三'; -- 3. 李四余额增加 1000 UPDATE account SET money = money + 1000 WHERE name = '李四';
执行后数据状态:张三
money
值从 2000 变为 1000,李四money
值从 2000 变为 3000,数据一致。异常情况测试 第三步执行错误时的 SQL:
-- 1. 查询张三余额 SELECT * FROM account WHERE name = '张三'; -- 2. 张三余额减少 1000 UPDATE account SET money = money - 1000 WHERE name = '张三'; -- 3. 李四余额增加 1000(此处出错) UPDATE account SET money = money + 1000 WHERE name = '李四';
执行后数据状态:张三
money
值变为 1000,李四money
值仍为 2000,数据不一致。
6.2.2 控制事务方式一
通过修改自动提交行为实现手动控制:
-- 查看当前自动提交设置
SELECT @@autocommit;
-- 关闭自动提交(0:手动提交)
SET @@autocommit = 0;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
此方法将 DML 语句的执行从默认自动提交改为手动提交,需显式调用 COMMIT
或 ROLLBACK
确认或撤销操作。
6.2.2 控制事务方式二
显式声明事务边界:
-- 开启事务
START TRANSACTION; -- 或 BEGIN;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
转账案例实现:
START TRANSACTION;
-- 核心操作与未控制事务相同
SELECT * FROM account WHERE name = '张三';
UPDATE account SET money = money - 1000 WHERE name = '张三';
UPDATE account SET money = money + 1000 WHERE name = '李四';
-- 根据执行结果提交或回滚
COMMIT; -- 成功时执行
ROLLBACK; -- 失败时执行
此方式通过 START TRANSACTION
明确界定事务范围,结合分支逻辑保证操作原子性。
6.3 事务四大特性(ACID)
事务行为通过以下特性保障数据可靠性:
- 原子性(Atomicity):事务是最小操作单元,整体成功或失败。
- 一致性(Consistency):事务完成时所有数据保持逻辑一致。
- 隔离性(Isolation):事务在独立环境下运行,不受并发操作干扰。
- 持久性(Durability):事务提交后对数据的修改永久生效。
6.4 并发事务问题
多事务并发访问时可能引发以下问题:
脏读(Dirty Read):一个事务读到另外一个事务还没有提交的数据
事务 B 读取事务 A 未提交的修改数据(如事务 A 修改后回滚,事务 B 读到无效数据)。
不可重复读(Non-Repeatable Read):一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
事务 A 多次读取同一条记录时,因事务 B 修改并提交导致结果不一致。
幻读(Phantom Read):一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 " 幻影 "。
事务 A 基于条件查询无记录,但事务 B 插入新数据并提交后,事务 A 插入相同记录时失败。
6.5 事务隔离级别
为解决并发问题,数据库提供不同隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | √ | √ | √ |
Read Committed | ✕ | √ | √ |
Repeatable Read(默认) | ✕ | ✕ | √ |
Serialzable | ✕ | ✕ | ✕ |
- Read Uncommitted:可读取未提交数据,可能导致所有并发问题,性能最高但安全性最低。
- Read Committed:仅读取已提交数据,避免脏读但仍存在不可重复读和幻读。
- Repeatable Read(MySQL 默认):保证同事务内多次读取结果一致,避免脏读和不可重复读,但可能发生幻读。
- Serializable:强制事务串行执行,避免所有并发问题,安全性最高但性能最低。
隔离级别操作语句:
-- 查询当前隔离级别
SELECT @@TRANSACTION_ISOLATION;
-- 设置隔离级别(SESSION:当前会话;GLOBAL:全局生效)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
隔离级别越高,数据一致性越强,但并发性能越低,需根据场景权衡选择。