🔥 MySQL 事务详解(超详细讲解)
🧭 一、事务是什么?
事务(Transaction) 是数据库操作的最小执行单元。事务中包含多条 SQL,这些 SQL 要么全部执行成功(提交 COMMIT
),要么在某条失败时全部撤销(回滚 ROLLBACK
)。
✅ 你可以这样理解:
就像 Java 中的 try-catch-finally,只不过这里是在数据库层。
🔐 二、事务的四大特性(ACID)
特性 | 含义 | 示例 |
---|---|---|
A - 原子性 | 一组操作是不可分割的整体 | 转账必须两个账户同时操作成功或失败 |
C - 一致性 | 操作前后数据都必须满足业务规则 | 钱没丢、没多出来,余额总和一致 |
I - 隔离性 | 多个事务互不干扰 | 你读我还没提交的数据?No way! |
D - 持久性 | 提交后的数据永久保存 | 哪怕断电、重启也不能丢 |
⚙️ 三、MySQL 如何使用事务?
MySQL 默认使用 自动提交模式(autocommit=1),即每条 SQL 自动 COMMIT
,所以事务要 手动开启。
-- 开启事务(等价于 BEGIN)
START TRANSACTION;
-- SQL 操作
UPDATE account SET balance = balance - 500 WHERE id = 1;
UPDATE account SET balance = balance + 500 WHERE id = 2;
-- 提交事务
COMMIT;
-- 如果出错,可以回滚
ROLLBACK;
📌
ROLLBACK
表示撤销本事务中所有未提交的更改。
💡 四、事务示例:银行转账
假设我们有一个 account
表:
CREATE TABLE account (
id INT PRIMARY KEY,
name VARCHAR(20),
balance DECIMAL(10,2)
);
事务操作流程:
START TRANSACTION;
-- 小池转500给小明
UPDATE account SET balance = balance - 500 WHERE name = '小池';
UPDATE account SET balance = balance + 500 WHERE name = '小明';
COMMIT;
如果第2步失败(比如账户不存在):
ROLLBACK;
🧠 五、事务的隔离级别(并发控制关键)
MySQL 使用 InnoDB 存储引擎时,支持以下 隔离级别:
等级 | 问题解决 | 可能问题 |
---|---|---|
READ UNCOMMITTED | 无 | 脏读、不可重复读、幻读 |
READ COMMITTED | 脏读 ❌ | 不可重复读、幻读 |
REPEATABLE READ(默认) | 脏读 ❌ 不可重复读 ❌ | 幻读(可用 gap lock 解决) |
SERIALIZABLE | 所有问题都 ❌ | 并发最低、锁多 |
🧪 六、常见并发问题举例
1. 脏读(Dirty Read)
A 读取了 B 尚未提交的修改,一旦 B 回滚,A 读的是无效数据。
2. 不可重复读(Non-repeatable Read)
A 两次读取同一行数据,B 在中间修改并提交,A 结果不一致。
3. 幻读(Phantom Read)
A 查询到的数据行数不同,因为 B 在中间插入了新的符合条件的数据。
🛠 七、事务 + Java 实战(JDBC + Spring)
✅ JDBC 手动控制事务
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // 关闭自动提交
// 执行 SQL
PreparedStatement ps1 = conn.prepareStatement("UPDATE account SET balance = balance - ? WHERE id = ?");
ps1.setBigDecimal(1, new BigDecimal("500"));
ps1.setInt(2, 1);
ps1.executeUpdate();
PreparedStatement ps2 = conn.prepareStatement("UPDATE account SET balance = balance + ? WHERE id = ?");
ps2.setBigDecimal(1, new BigDecimal("500"));
ps2.setInt(2, 2);
ps2.executeUpdate();
conn.commit(); // 所有成功,提交
} catch (Exception e) {
conn.rollback(); // 失败则回滚
e.printStackTrace();
}
✅ Spring 声明式事务(最推荐)
用 @Transactional
一键控制事务:
@Service
public class TransferService {
@Autowired
AccountMapper accountMapper;
@Transactional
public void transfer(int fromId, int toId, BigDecimal amount) {
accountMapper.decreaseBalance(fromId, amount);
accountMapper.increaseBalance(toId, amount);
// 如果中间抛异常,Spring 会自动 rollback
}
}
注意:
默认事务回滚的是运行时异常(
RuntimeException
);如果要回滚 checked 异常,可以这样配置:
@Transactional(rollbackFor = Exception.class)
📌 八、事务与存储过程
MySQL 的存储过程也支持事务控制:
CREATE PROCEDURE transfer(IN from_id INT, IN to_id INT, IN amt DECIMAL(10,2))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
START TRANSACTION;
UPDATE account SET balance = balance - amt WHERE id = from_id;
UPDATE account SET balance = balance + amt WHERE id = to_id;
COMMIT;
END;
✅ 总结:事务开发注意事项
建议 | 说明 |
---|---|
开启事务前关闭自动提交 | conn.setAutoCommit(false) |
所有操作成功后再 commit() |
别中途提交 |
捕获异常时记得 rollback() |
防止数据不一致 |
数据库隔离级别合理配置 | 默认够用,但高并发时需调优 |
@Transactional 最省心 | Spring 自动帮你控制事务 |