MySQL: MySQL锁机制与MVCC原理

最后更新:2026-08-26

锁是并发控制的核心——理解锁机制才能写出高效的并发代码。

本课讲解 MySQL 的锁机制。

1. 你将学到


2. 锁的分类

100%
graph TB
    A[MySQL 锁] --> B[按粒度]
    A --> C[按类型]
    B --> D[表锁 Table Lock]
    B --> E[行锁 Row Lock]
    B --> F[页锁 Page Lock]
    C --> G[共享锁 S Lock]
    C --> H[排他锁 X Lock]
    C --> I[意向锁 Intention Lock]
维度 表锁 行锁
粒度 整张表 单行
冲突概率
并发性能
开销
引擎 MyISAM/InnoDB InnoDB

3. 共享锁与排他锁

(1) 共享锁(S Lock)

SQL
-- 加共享锁(读锁)— MySQL 8.0+ 推荐语法
SELECT * FROM users WHERE id = 1 FOR SHARE;

-- MySQL 5.7 旧语法(8.0 已废弃但仍可用)
-- SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;

-- 其他事务可以读,不能写

(2) 排他锁(X Lock)

SQL
-- 加排他锁(写锁)
SELECT * FROM users WHERE id = 1 FOR UPDATE;

-- 其他事务不能读(如果也加锁)也不能写
锁类型
共享锁 S
排他锁 X

4. 意向锁

意向锁是表级锁,用于快速判断表中是否有行锁。

意向锁 说明
IS(意向共享锁) 事务打算给行加 S 锁
IX(意向排他锁) 事务打算给行加 X 锁

作用: 加表锁时无需逐行检查,只需检查意向锁。

▶ 示例:锁的实际应用

SQL
-- 会话1:加排他锁
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 此时 id=1 被锁住

-- 会话2:尝试更新(等待)
UPDATE accounts SET balance = 500 WHERE id = 1;
-- 等待直到会话1提交或超时

-- 会话1:提交
COMMIT;
-- 会话2立即执行成功
▶ 试一试

5. MVCC 多版本并发控制

MVCC 通过保存数据的多个版本,实现无锁读取。

(1) MVCC 原理

100%
graph LR
    A[当前数据] --> B[Undo Log<br/>历史版本链]
    B --> C[v1] --> D[v2] --> E[v3]
    F[Read View] -->|根据隔离级别| G[读取哪个版本]

(2) 不同隔离级别的读取行为

隔离级别 读取方式
READ UNCOMMITTED 总是读最新(可能脏读)
READ COMMITTED 每次 SELECT 创建新 Read View
REPEATABLE READ 事务开始时创建 Read View
SERIALIZABLE 加锁读取

6. 死锁

(1) 什么是死锁

两个事务互相等待对方释放锁:

SQL
-- 事务A
START TRANSACTION;
UPDATE accounts SET balance = 100 WHERE id = 1;  -- 锁住 id=1
UPDATE accounts SET balance = 200 WHERE id = 2;  -- 等待 id=2

-- 事务B
START TRANSACTION;
UPDATE accounts SET balance = 300 WHERE id = 2;  -- 锁住 id=2
UPDATE accounts SET balance = 400 WHERE id = 1;  -- 等待 id=1(死锁!)

(2) 死锁处理

SQL
-- 查看死锁信息
SHOW ENGINE INNODB STATUS\G

-- 设置锁等待超时
SET innodb_lock_wait_timeout = 50;  -- 秒

(3) 避免死锁

策略 说明
固定顺序 按相同顺序访问表和行
减少锁持有时间 事务尽快提交
使用低隔离级别 READ COMMITTED 锁更少
合理建索引 减少锁范围

7. 锁等待超时

SQL
-- 查看锁等待超时
SELECT @@innodb_lock_wait_timeout;

-- 设置(秒)
SET innodb_lock_wait_timeout = 10;

-- 超时报错
-- ERROR 1205: Lock wait timeout exceeded

❓ 常见问题

Q InnoDB 用表锁还是行锁?
A 默认用行锁(基于索引)。没有索引时退化为表锁。
Q SELECT 会加锁吗?
A 普通 SELECT 不加锁(MVCC 快照读)。SELECT ... FOR UPDATE 加排他锁,SELECT ... FOR SHARE(MySQL 8.0+)加共享锁。旧语法 LOCK IN SHARE MODE 在 8.0 中已废弃。
Q 如何查看当前锁?
A SELECT * FROM performance_schema.data_locks;(MySQL 8.0)
Q InnoDB 用表锁还是行锁?
A 默认行锁,基于索引实现。无索引时退化为表锁。
Q SELECT 会加锁吗?
A 普通 SELECT 不加锁(MVCC 快照读),FOR UPDATE 加排他锁,FOR SHARE 加共享锁。

📖 小节


📝 作业

  1. 基础题(难度⭐):演示共享锁和排他锁的区别。

  2. 进阶题(难度⭐⭐):构造一个死锁场景并观察 MySQL 的处理。

  3. 挑战题(难度⭐⭐⭐):对比不同隔离级别下的并发读写行为。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏