锁的世界,比你想象得更精彩!
在并发环境下,多线程操作数据库的同一份数据时,如果没有锁机制,可能会出现以下问题:
锁的存在,就是为了保证并发情况下的数据一致性与隔离性。
分类 | 说明 |
---|---|
全局锁(Global Lock) | 对整个数据库加锁,一般用于全库备份 |
表级锁(Table Lock) | 针对整张表加锁,粒度较大 |
行级锁(Row Lock) | 针对数据行加锁,粒度最小,性能最好,但实现最复杂 |
类型 | 示例 | 说明 |
---|---|---|
共享锁(S锁) | SELECT ... LOCK IN SHARE MODE |
允许多个事务同时读,不允许写 |
排他锁(X锁) | SELECT ... FOR UPDATE |
其他事务不能读写 |
意向锁(IS/IX锁) | 系统自动加 | 辅助锁,表级声明“我想要对某行加锁” |
存储引擎 | 锁类型 | 特性 |
---|---|---|
MyISAM | 表级锁 | 写阻塞读,读也阻塞写 |
InnoDB | 行级锁 + 表级锁 + 意向锁 | 支持事务,支持多版本并发控制(MVCC) |
❗注意:InnoDB 的行锁是加在索引上的,而不是行数据本身!
类型 | 锁定对象 | 应用场景 |
---|---|---|
Gap Lock | 索引之间的间隙,不含记录本身 | 防止幻读 |
Record Lock | 精确锁定某一行 | WHERE id = 1 |
Next-Key Lock | Gap Lock + Record Lock | 范围查询:WHERE id > 10 AND id < 20 |
意向锁是表级的,辅助事务判断是否可以加表锁。
IS
:意向共享锁IX
:意向排他锁不会阻塞其它行锁,但可以提高加锁效率
-- 加共享锁
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 加排他锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;
START TRANSACTION;
UPDATE users SET name = '张三' WHERE id = 1;
-- 自动加X锁
COMMIT;
-- 假设id上有索引
SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;
-- 实际锁住:记录10~20之间所有行 + 所在间隙
因为你的 WHERE
条件可能是范围查询,比如:
UPDATE users SET status = 'active' WHERE score > 80;
会触发 Next-Key Lock 锁住符合范围的所有行+间隙。
-- 查看 InnoDB 锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 会话1:
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 会话2:
BEGIN;
UPDATE account SET balance = balance - 200 WHERE id = 2;
-- 会话1:
UPDATE account SET balance = balance + 100 WHERE id = 2; -- 阻塞
-- 会话2:
UPDATE account SET balance = balance + 200 WHERE id = 1; -- 死锁!
解决方式:加锁顺序保持一致!
你真的了解 MySQL 锁吗?为什么 update 有时候卡住?死锁频频出现,根源在哪?一文彻底搞懂 MySQL 锁机制,从底层原理到实战演练,助你迈向高级工程师之路!
锁类型 | 粒度 | 并发性 | 实现方式 |
---|---|---|---|
表锁 | 粗 | 差 | MyISAM |
行锁 | 细 | 高 | InnoDB(基于索引) |
Gap Lock | 间隙锁 | 防幻读 | Next-Key Lock 衍生 |
锁是一把双刃剑:加得对,性能飞起;加得错,性能雪崩!