是,但仅限于能用上索引的条件;若where字段无索引,则锁所有扫描的聚簇索引记录,可能等效表级锁。

MySQL 默认隔离级别下,UPDATE 语句会自动加行锁吗?
会,但仅限于能用上索引的条件。如果 WHERE 子句中的字段没有索引,InnoDB 会退化为锁全表(更准确地说,是锁所有扫描到的聚簇索引记录,可能等效于表级锁)。
-
UPDATE t SET status = 1 WHERE id = 100:id 是主键 → 只锁该行 -
UPDATE t SET status = 1 WHERE name = 'alice':name 无索引 → 扫描全表,每行都加临键锁(next-key lock),并发更新极易阻塞 - 即使有索引,若查询走的是
range而非const,也会锁住范围内的所有间隙和记录,比如WHERE id BETWEEN 10 AND 20
REPEATABLE READ 隔离级别下,为什么还会出现“幻读”?
严格来说,InnoDB 在 REPEATABLE READ 下通过临键锁(next-key lock)基本消除了“幻读”,但仅限于当前读(SELECT ... FOR UPDATE、UPDATE、DELETE 等)。而快照读(普通 SELECT)不加锁,依赖 MVCC,看到的是事务启动时的一致性视图 —— 这种“读到新插入的行”现象常被误称为幻读,实际是 MVCC 的正常行为。
- 幻读只在“当前读”场景下被临键锁阻止;快照读看到新数据不是 bug,是设计如此
- 如果业务逻辑依赖“两次 SELECT 结果一致”,不能只靠隔离级别,得显式加锁,例如
SELECT ... FOR UPDATE - MySQL 8.0+ 支持
READ COMMITTED下也用临键锁(需开启innodb_locks_unsafe_for_binlog=OFF,但默认已弃用该参数)
如何避免死锁?关键看 SQL 执行顺序和索引覆盖
死锁本质是两个事务以不同顺序申请同一组资源锁。InnoDB 检测到循环等待后会回滚其中一个事务(通常代价小的那个),抛出 Deadlock found when trying to get lock 错误。
- 统一 DML 操作的执行顺序:比如总是先更新
orders表,再更新inventory表 - 确保 WHERE 条件走相同索引路径,避免一个走主键、另一个走二级索引导致锁粒度/顺序不一致
- 用
EXPLAIN检查执行计划,确认是否走了预期索引;必要时加FORCE INDEX - 减少事务内 SQL 数量,避免长事务;不要在事务中做 RPC、文件读写等外部耗时操作
INSERT … ON DUPLICATE KEY UPDATE 和 REPLACE INTO 并发安全吗?
INSERT ... ON DUPLICATE KEY UPDATE 是并发安全的,它会在冲突的唯一键(主键或 UNIQUE 索引)上加意向锁 + 行锁,并原子性完成“检测→更新”;而 REPLACE INTO 实际是“DELETE + INSERT”,在高并发下可能引发间隙锁冲突、主键自增跳号,甚至触发外键级联删除等意外行为。
-
REPLACE INTO在遇到重复键时,会先尝试加 X 锁删旧行,若行不存在则加插入意向锁,容易与其它事务的间隙锁冲突 - 如果表有多个唯一索引,
REPLACE可能因锁顺序不一致导致死锁;ON DUPLICATE KEY UPDATE只基于第一个匹配的唯一索引做处理 - 当需要“存在则更新,不存在则插入”时,优先用
INSERT ... ON DUPLICATE KEY UPDATE,而不是REPLACE INTO或先SELECT再判断










