一、MySQL锁
当多个用户或进程并发操作数据库时,为了避免数据冲突、脏读、不可重复读、幻读等问题,数据库通过锁机制来保证数据的一致性和完整性。
MySQL 锁的总体分类:
分类维度 | 类型 | 说明 |
---|---|---|
按作用对象 | 表级锁(Table Lock) | 锁住整张表 |
行级锁(Row Lock) | 锁住一行数据 | |
按锁粒度 | 全局锁、数据库锁、表锁、行锁 | 粒度逐级细化 |
按加锁方式 | 共享锁(S锁) | 允许读,不允许写 |
排他锁(X锁) | 允许写,其他读写都被阻塞 | |
按使用方式 | 显式锁、隐式锁 | 手动加锁 or 自动加锁 |
按存储引擎实现 | 不同引擎的锁实现机制不同 | InnoDB vs MyISAM |
二、MySQL 中的锁类型
1. 表级锁(Table Lock)
- 作用于整张表;
- 典型场景:
MyISAM
存储引擎、ALTER TABLE
、LOCK TABLES
; - 效率高、开销小,但并发性差。
示例:
-- 给表 user 加上 写锁(WRITE LOCK)。
LOCK TABLES user WRITE;
-- 操作
UNLOCK TABLES;
2. 行级锁(Row Lock)
- 作用于某一行数据;
InnoDB
引擎支持,适合高并发读写场景;- 粒度小,并发性好,但管理成本高。
示例——隐式加锁:
BEGIN;
SELECT * FROM orders WHERE id = 10 FOR UPDATE; -- X锁
COMMIT;
3. InnoDB
的事务级锁(最常用)
(1)共享锁(S锁, Shared Lock)
- 多个事务可以并发加 S 锁,读取数据;
- 但不能加 X 锁,写操作会被阻塞。
示例:
SELECT * FROM products WHERE id=1 LOCK IN SHARE MODE;
注意:
- 为查询到的行加上共享锁(S锁),直到事务结束(提交或回滚)。
(2)排他锁(X锁, Exclusive Lock)
- 独占锁:读写都阻塞其他事务;
- 常用于修改数据。
示例:
SELECT * FROM products WHERE id=1 FOR UPDATE;
注意:
- 查询的同时,对满足条件的记录加排他锁,防止其他事务对该记录进行修改或加锁读取。
(3)意向锁(Intention Lock)
InnoDB
自动加的表级锁,用于指示事务是否准备加行锁;- 无需手动操作;
- 分为:意向共享锁(IS)、意向排他锁(IX)。
4. 其他特殊类型
(1)Gap Lock(间隙锁)
- 锁住的是数据之间的“间隙”,防止幻读;
- 例如:
SELECT * FROM t WHERE age > 30 FOR UPDATE;
。
(2)Next-Key Lock(记录锁 + 间隙锁)
InnoDB
默认的锁,避免幻读;- 锁住“记录本身 + 相邻间隙”。
(3)自增长锁(Auto-Inc Lock)
- 针对
AUTO_INCREMENT
字段的内部锁; - 防止并发插入冲突。
三、锁的工作原理(以 InnoDB
为例)
- 当事务执行写操作时,
InnoDB
会在相关行上加排他锁(X锁)。 - 读操作默认不加锁(基于 MVCC 实现快照读),除非使用
SELECT ... FOR UPDATE
或LOCK IN SHARE MODE
强制加锁。 - 当一个事务对某行加了 X 锁,其他事务必须等待锁释放后才能访问该行。
四、常见锁相关语句
SELECT ... FOR UPDATE
:对查询到的行加排他锁。SELECT ... LOCK IN SHARE MODE
:对查询到的行加共享锁。LOCK TABLES
和UNLOCK TABLES
:对表加锁和解锁(表锁)。
五、常见问题
1. 死锁(Deadlock)
-
多个事务互相等待对方持有的锁,导致程序无限等待。
-
InnoDB 能自动检测死锁,并回滚其中一个事务。
-
解决策略:
- 保持事务尽量短小;
- 尽量避免长时间锁住资源;
- 按照固定顺序访问表和行。
2. 脏写(Dirty Write)
定义:
一个事务写入了另一个未提交事务修改的数据,这会导致数据污染或逻辑错误。
示例:
-- 事务A:
BEGIN;
UPDATE users SET name = 'Alice' WHERE id = 1;-- 事务B 在事务A未提交时:
BEGIN;
UPDATE users SET name = 'Bob' WHERE id = 1;
注意:
- 如果数据库允许这样操作,就是脏写。
- 假如事务A回滚了,但事务B的修改已提交,数据将不一致。
解决:
- MySQL
InnoDB
的默认隔离级别REPEATABLE READ
已避免脏写。 - 即使设置为
READ COMMITTED
,也不允许脏写。
3. 幻读(Phantom Read)
定义:
在同一个事务中,前后两次相同条件的查询,返回的记录数不一样,即第二次出现了“幻影”数据。
示例:
-- 事务A:
BEGIN;
SELECT * FROM orders WHERE amount > 1000;-- 事务B 插入新数据:
INSERT INTO orders(amount) VALUES(2000); -- 满足条件-- 事务A 再次执行:
SELECT * FROM orders WHERE amount > 1000;
-- 返回了比上次多一行
注意:
- 在
READ COMMITTED
或REPEATABLE READ
级别下会出现幻读。
六、锁的隔离级别
隔离级别 | 说明 | 是否使用行锁 |
---|---|---|
READ UNCOMMITTED | 允许脏读 | 无锁或极少 |
READ COMMITTED | 不允许脏读 | 读操作不加锁(快照读) |
REPEATABLE READ | 可重读,InnoDB 默认隔离级别 | 读操作快照读,写操作加行锁 |
SERIALIZABLE | 串行化,最高隔离级别,所有读操作加锁 | 读写都加锁 |