Skip to content

MySQL 事务与锁机制

事务的四大特性(ACID)

特性描述实现方式
原子性(Atomicity)事务是不可分割的最小单位Undo Log
一致性(Consistency)事务前后数据保持一致由其他三个特性保证
隔离性(Isolation)事务之间互不干扰MVCC + 锁
持久性(Durability)事务提交后永久保存Redo Log

事务隔离级别

并发事务问题

问题描述
脏读读取到其他事务未提交的数据
不可重复读同一事务中,两次读取同一数据结果不同(被其他事务修改)
幻读同一事务中,两次查询的记录数不同(被其他事务插入/删除)

四种隔离级别

隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ(MySQL 默认)⚠️ 部分解决
SERIALIZABLE
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

MVCC 多版本并发控制

MVCC(Multi-Version Concurrency Control)通过保存数据的历史版本,实现读写不冲突。

核心组件

  1. 隐藏字段:每行数据有两个隐藏列

    • DB_TRX_ID:最后修改该行的事务 ID
    • DB_ROLL_PTR:指向 Undo Log 的指针
  2. Undo Log:记录数据的历史版本,形成版本链

  3. Read View:事务开始时生成的快照,决定能看到哪些版本

Read View 工作原理

Read View 包含:
- m_ids:当前活跃的事务 ID 列表
- min_trx_id:最小活跃事务 ID
- max_trx_id:下一个将要分配的事务 ID
- creator_trx_id:创建该 Read View 的事务 ID

可见性判断规则

  1. trx_id == creator_trx_id → 可见(自己修改的)
  2. trx_id < min_trx_id → 可见(事务已提交)
  3. trx_id >= max_trx_id → 不可见(事务在 Read View 之后开启)
  4. trx_id in m_ids → 不可见(事务未提交)

锁机制

锁的分类

按粒度分:
├── 表级锁
│   ├── 表锁(Table Lock)
│   ├── 元数据锁(MDL)
│   └── 意向锁(Intention Lock)
└── 行级锁
    ├── 记录锁(Record Lock)
    ├── 间隙锁(Gap Lock)
    └── 临键锁(Next-Key Lock)

行级锁详解

1. 记录锁(Record Lock)

锁定单条记录。

sql
-- 对 id=1 的记录加锁
SELECT * FROM user WHERE id = 1 FOR UPDATE;

2. 间隙锁(Gap Lock)

锁定索引记录之间的间隙,防止幻读。

sql
-- 假设表中有 id: 1, 5, 10
-- 以下语句会锁住 (5, 10) 这个间隙
SELECT * FROM user WHERE id > 5 AND id < 10 FOR UPDATE;

3. 临键锁(Next-Key Lock)

记录锁 + 间隙锁的组合,是 InnoDB 默认的行锁算法。

sql
-- 锁定 (5, 10] 区间
SELECT * FROM user WHERE id = 10 FOR UPDATE;

死锁

当两个或多个事务互相等待对方释放锁时,就会发生死锁。

sql
-- 事务 A
BEGIN;
UPDATE user SET name = 'A' WHERE id = 1;  -- 持有 id=1 的锁
UPDATE user SET name = 'A' WHERE id = 2;  -- 等待 id=2 的锁

-- 事务 B
BEGIN;
UPDATE user SET name = 'B' WHERE id = 2;  -- 持有 id=2 的锁
UPDATE user SET name = 'B' WHERE id = 1;  -- 等待 id=1 的锁 → 死锁!

解决方案

  • InnoDB 自动检测死锁,回滚较小的事务
  • 按固定顺序访问资源
  • 设置锁等待超时:innodb_lock_wait_timeout