mysql数据库锁的基本概念_mysql锁入门解析

来源:这里教程网 时间:2026-02-28 20:45:55 作者:

锁到底锁的是什么?不是数据行,而是索引项

MySQL 的行级锁(尤其是 InnoDB)**不直接锁数据行,而是锁索引上的记录**。哪怕你没建任何索引,InnoDB 也会创建一个隐藏的聚簇索引(

GEN_CLUST_INDEX
),所有行锁都落在这个索引结构上。

这意味着:

用主键(如
WHERE id = 100
)更新,只在聚簇索引上加一把
X
锁;
用二级索引(如
WHERE name = 'Alice'
)更新,则先在
name
索引上加
X
锁,再回表到聚簇索引上对对应主键加
X
锁——共两把锁;
如果
WHERE
条件没走索引(例如
WHERE status+0 = 1
LIKE '%abc'
),InnoDB 无法精准定位,会退化为扫描全表,并对**每条匹配记录的聚簇索引项加锁**,极端情况下等效于锁整张表。

共享锁 vs 排他锁:什么时候该用
LOCK IN SHARE MODE

S
锁(共享锁)和
X
锁(排他锁)是底层基础,但日常开发中你几乎不会手动加
S
锁——除非你需要显式阻塞其他写操作,同时允许并发读。

典型场景是「防超卖」中的读-改-写闭环:

普通
SELECT
是一致性读(MVCC),不加锁,可能读到旧库存;
SELECT stock FROM goods WHERE id = 123 LOCK IN SHARE MODE
,能确保读到当前最新值,且阻止其他事务对这行加
X
锁(即不能扣减);
后续紧跟
UPDATE goods SET stock = stock - 1 WHERE id = 123
,此时已持有
S
锁,InnoDB 会自动升级为
X
锁完成更新;
若跳过
LOCK IN SHARE MODE
直接
UPDATE
,虽也加
X
锁,但中间存在窗口:两次请求可能同时读到 stock=1,然后都执行 -1 → 变成 -1。

别被“行锁”骗了:为什么
UPDATE
有时卡住整张表?

InnoDB 的“行锁”只是默认行为,**是否真锁单行,完全取决于执行计划是否命中索引**。

常见踩坑点:

UPDATE users SET status = 1 WHERE phone LIKE '%138%'
:无索引 + 模糊前缀 → 全表扫描 → 对每行聚簇索引加
X
锁 → 等效表锁;
UPDATE orders SET paid = 1 WHERE created_at > '2025-01-01'
:若
created_at
无索引,同样锁全表;
复合查询中用了函数:
WHERE DATE(create_time) = '2025-01-01'
→ 索引失效 → 锁范围扩大;
即使有索引,若统计信息过期(
ANALYZE TABLE
未执行),优化器也可能误判为全表扫描。

全局锁
FLUSH TABLES WITH READ LOCK
的真实代价

它确实能保证备份一致性,但代价是整个实例只读——所有 DML、DDL、甚至

COMMIT
都会被阻塞,业务写入直接挂起。

所以生产环境慎用,尤其高流量系统:

mysqldump --single-transaction
是更优解(依赖 MVCC,仅对 InnoDB 有效),它不加全局锁,靠事务快照保证一致性;
FLUSH TABLES WITH READ LOCK
主要用于 MyISAM 表或混合引擎库的备份;
执行后必须配对
UNLOCK TABLES
,否则锁一直挂着——曾有案例因忘记解锁,导致线上订单积压数小时;
注意:它不阻塞
SELECT
,但会阻塞任何修改元数据的操作(比如
ALTER TABLE
),而这类操作常被运维后台静默触发。

锁机制不是黑盒,它的行为直接受索引设计、SQL 写法、隔离级别共同决定。最危险的不是锁本身,而是你以为加了行锁,实际锁了一片索引范围,甚至整张表。

相关推荐