Mysql--基础知识点--100-- insert VS select...for update 加锁

INSERT vs SELECT ... FOR UPDATE 锁机制完整对比总结

一、锁全景对比表

维度 INSERT SELECT ... FOR UPDATE
核心目的 插入新数据,保证唯一性与数据完整性 锁定已有数据,防止并发修改/删除,并避免幻读
表级意向锁 ✅ 加 IX(意向排他锁),必加 ✅ 加 IX(意向排他锁),必加
自增锁 ✅ 可能有(表级 AUTO-INC 锁或轻量级互斥锁),取决于 innodb_autoinc_lock_mode 和语句类型 ❌ 无
间隙锁 通过 插入意向锁X, GAP, INSERT_INTENTION)标记间隙,仅用于协调并发插入 根据隔离级别和查询条件,可能加 间隙锁X, GAP)或 Next-Key 锁X),用于阻止幻读
记录锁 ✅ 插入成功后加 排他记录锁X, REC_NOT_GAP),锁定新行 ✅ 对扫描到的每一行加 排他记录锁X, REC_NOT_GAP
锁定对象 新插入的行 + 插入位置的间隙意向 查询条件匹配的所有现有行 + 查询范围覆盖的间隙(取决于条件)
锁范围 极窄:仅新行及其间隙意向 可能很宽:全表、范围或单行(取决于索引使用情况)
对并发插入的影响 包容:同一间隙不同位置的并发 INSERT 互不阻塞 阻塞:间隙锁会阻止其他事务在锁定范围内插入新行
对并发更新/删除的影响 新行被排他锁保护,其他事务不能修改/删除它 锁定的行被排他锁保护,其他事务不能修改/删除它们
死锁风险 较低,但可能在唯一键冲突或间隙锁阻塞时发生 较高,尤其是多表/多范围操作
典型应用场景 数据写入、批量导入 行级锁定读、乐观锁检查、防超卖

二、关键差异深度解析

1. 意向锁(IX)------ 两者相同
  • 都加表级 IX 锁 :在任何行级写操作(INSERTUPDATEDELETESELECT ... FOR UPDATE)之前,InnoDB 都会自动在表上加 IX 锁。
  • 作用 :声明事务将要修改某些行,用于快速判断与表级锁(如 LOCK TABLES ... WRITE)的冲突。
  • 兼容性:多个 IX 锁之间完全兼容,不阻塞行级并发。
2. 自增锁(AUTO-INC)------ INSERT 特有
  • 何时出现 :仅当表包含 AUTO_INCREMENT 列且 INSERT 需要生成新自增值时。
  • 锁类型
    • 表级自增锁AUTO-INC):在 innodb_autoinc_lock_mode = 0(传统模式)或 =1(连续模式)下执行 INSERT ... SELECT 等行数不确定的语句时出现,语句结束后释放。
    • 轻量级互斥锁 :在默认模式(=1)下单行或确定行数的批量插入时使用,非传统锁,不出现于 data_locks 中。
  • SELECT ... FOR UPDATE:完全不涉及自增锁。
3. 间隙锁与插入意向锁 ------ 本质不同
INSERT SELECT ... FOR UPDATE
锁名称 插入意向锁(X, GAP, INSERT_INTENTION 间隙锁(X, GAP)或 Next-Key 锁(X
目的 声明"我打算在此间隙插入",允许其他插入意向锁共存 声明"此间隙禁止插入",阻塞所有插入意向锁
兼容性 插入意向锁之间兼容 间隙锁与任何插入意向锁互斥
是否锁定记录 否(仅间隙意向) 否(间隙锁不锁记录),但 Next-Key 锁会锁记录

简记:插入意向锁是"我要进来",间隙锁是"此路不通"。

4. 行锁(排他记录锁)------ 两者都有,但时机不同
  • INSERT :插入成功后才加排他记录锁,保护新行。
  • SELECT ... FOR UPDATE扫描时立即对现有行加排他记录锁,保护读取到的行。

三、锁的兼容性矩阵(行级锁部分)

锁类型 插入意向锁 间隙锁 Next-Key 锁 排他记录锁
插入意向锁 ✅ 兼容 ❌ 冲突 ❌ 冲突 ✅ 兼容(不同行)
间隙锁 ❌ 冲突 ✅ 兼容 ✅ 兼容 ✅ 兼容
Next-Key 锁 ❌ 冲突 ✅ 兼容 ❌ 冲突 ❌ 冲突
排他记录锁 ✅ 兼容 ✅ 兼容 ❌ 冲突 ❌ 冲突

说明:✅ 表示两个锁可以同时被不同事务持有;❌ 表示其中一个必须等待另一个释放。


四、验证方法(使用 performance_schema.data_locks

sql 复制代码
-- 事务1
BEGIN;
INSERT INTO t (id) VALUES (10);   -- 或 SELECT ... FOR UPDATE

-- 事务2(观察锁)
SELECT ENGINE_TRANSACTION_ID, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, LOCK_DATA
FROM performance_schema.data_locks
WHERE OBJECT_NAME = 't';

典型输出差异

操作 表级锁 行级锁(示例)
INSERT (自增列,模式1) TABLE, IX RECORD, X,INSERT_INTENTION RECORD, X,REC_NOT_GAP
SELECT ... FOR UPDATE (范围查询) TABLE, IX RECORD, X (Next-Key) 或 RECORD, X,GAP
INSERT ... SELECT (模式0) TABLE, IX TABLE, AUTO_INC INSERT

五、实际开发建议

  1. 优先使用 INSERT 的天然并发性 :对于单纯的数据写入,不要额外加 SELECT ... FOR UPDATE,避免不必要的间隙锁阻塞。
  2. 需要"防插入"时用 SELECT ... FOR UPDATE:例如实现"唯一库存扣减"前,需锁定范围防止幻读。
  3. 注意自增锁模式 :高并发写入场景,建议 innodb_autoinc_lock_mode = 2(交错模式),但需确保 binlog 格式为 ROW
  4. 死锁排查INSERTSELECT ... FOR UPDATE 混合使用时,注意间隙锁与插入意向锁的互斥关系,极易产生死锁。

一句话总结
INSERTIX + 插入意向锁 + 排他记录锁 (以及可能的自增锁),主打高并发写入;
SELECT ... FOR UPDATEIX + 间隙锁/Next-Key锁 + 排他记录锁 ,主打强一致锁定读。

两者共享意向锁,但间隙锁与插入意向锁互斥,这是并发冲突与死锁的核心根源。

相关推荐
2301_777599372 小时前
golang如何实现WebSocket断线重连_golang WebSocket断线重连实现要点
jvm·数据库·python
ZeroNews内网穿透2 小时前
ZeroNews安全网关接入企业微信自建应用
网络·数据库·安全·云计算
数据雕塑家2 小时前
数据库 + Grafana 可视化配置指南:从数据源连接到第一个仪表盘
数据库·grafana
源图客2 小时前
Linux系统部署Postgres数据库(ubuntu22.04)
linux·运维·数据库
minebmw72 小时前
Oracle 19.29 中 ORA-00600 [4000] 错误完全解析
数据库·oracle
编程经验分享2 小时前
Windows 安装 PostgreSQL 并安装 vector 扩展
数据库·postgresql
ZPC82102 小时前
moveit servo 发指令给real arm
java·前端·数据库
小草儿7992 小时前
gbase8s之系统表sysprocedures
数据库
爱莉希雅&&&2 小时前
MySQL 高可用实战:PXC + HAProxy + Keepalived 完整版笔记
运维·数据库·mysql·haproxy·数据库同步·pxc