MySQL 事务与锁机制
TIP
事务保证了一组数据库操作要么全部成功要么全部回滚。理解事务隔离级别和锁机制对避免数据不一致至关重要。
ACID 特性
| 特性 | 说明 |
|---|---|
| Atomicity(原子性) | 事务内的操作要么全做,要么全不做 |
| Consistency(一致性) | 事务前后数据完整性约束不变 |
| Isolation(隔离性) | 并发事务互不干扰 |
| Durability(持久性) | 提交后数据永久保存 |
事务操作
sql
-- 开启事务
START TRANSACTION;
-- 业务操作
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 提交(成功)
COMMIT;
-- 回滚(失败)
ROLLBACK;
-- 设置保存点
SAVEPOINT sp1;
ROLLBACK TO sp1;隔离级别
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ |
| READ COMMITTED | ✗ | ✓ | ✓ |
| REPEATABLE READ(默认) | ✗ | ✗ | ✓ |
| SERIALIZABLE | ✗ | ✗ | ✗ |
行锁与表锁
sql
-- 行级锁:InnoDB 默认
-- ❌ 不走索引会导致行锁升级为表锁
UPDATE user SET age = 20 WHERE name = '张三'; -- name 无索引 → 表锁
-- 表级锁:手动加锁
LOCK TABLES user READ;
LOCK TABLES user WRITE;
UNLOCK TABLES;
-- 间隙锁(Gap Lock):REPEATABLE READ 级别防止幻读死锁
java
// 死锁示例
// 事务A:UPDATE account SET balance=100 WHERE id=1;
// 事务B:UPDATE account SET balance=200 WHERE id=2;
// 事务A:UPDATE account SET balance=100 WHERE id=2; -- 等待 B
// 事务B:UPDATE account SET balance=200 WHERE id=1; -- 等待 A
// MySQL 会自动检测并回滚其中一个事务
// 避免死锁:
// 1. 按固定顺序访问资源
// 2. 尽量缩短事务时间
// 3. 减少锁的持有时间MVCC(多版本并发控制)
InnoDB 通过 MVCC 实现非阻塞读:
- 每行记录保存多个版本
- 每个事务看到的是数据的快照(undo log)
- 实现 READ COMMITTED 和 REPEATABLE READ