MySQL 锁
锁分类
按粒度分:
├── 表锁:MyISAM,InnoDB 也支持
├── 行锁:InnoDB(精准到行)
└── 页面锁:BDB
按模式分:
├── 共享锁(S锁):读锁,多个事务可以同时持有
└── 排他锁(X锁):写锁,只能有一个事务持有
按算法分:
├── Record Lock:记录锁
├── Gap Lock:间隙锁
└── Next-Key Lock:记录锁+间隙锁1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
InnoDB 行锁
sql
-- 共享锁:其他事务可以读,但不能写
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 排他锁:其他事务不能读也不能写
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 意向锁(自动加):
-- 事务 A 获取某行 X 锁前,会先获取该表的 IX(意向排他锁)1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
间隙锁(Gap Lock)
防止幻读:
sql
-- 范围查询时,锁住索引之间的间隙
BEGIN;
SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE;
-- 锁住 age 在 20~30 之间的所有行,以及这些行之间的"间隙"
-- 即使 age=25 的记录不存在,间隙也会被锁住
COMMIT;1
2
3
4
5
6
2
3
4
5
6
死锁
sql
-- 事务 A 事务 B
BEGIN; BEGIN;
SELECT * FROM t1 FOR UPDATE; SELECT * FROM t2 FOR UPDATE;
UPDATE t1 SET name='a'; UPDATE t2 SET name='b';
UPDATE t2 SET name='a'; UPDATE t1 SET name='b'; -- 死锁!1
2
3
4
5
2
3
4
5
死锁处理
sql
-- 查看死锁日志
SHOW ENGINE INNODB STATUS;
-- InnoDB 会自动回滚最小的事务
-- 也可以手动处理
KILL connection_id;
-- 减少死锁:
-- 1. 按固定顺序访问表
-- 2. 小事务(减少锁定时间)
-- 3. 合理选择隔离级别1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
MVCC 与锁
sql
-- READ COMMITTED:每次 SELECT 生成新快照
BEGIN;
-- 其他事务提交了新数据,这里能看到
-- REPEATABLE READ(默认):事务开始时的快照
BEGIN;
-- 整个事务看到的数据都是一致的1
2
3
4
5
6
7
2
3
4
5
6
7
乐观锁与悲观锁
sql
-- 乐观锁(版本号)
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 1;
-- 悲观锁(SELECT FOR UPDATE)
BEGIN;
SELECT * FROM products WHERE id = 1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
[[返回 MySQL 首页|../index]]