Skip to content

MySQL 中有哪些锁类型? ​

  • 标签:后端、MySQL、数据库

🏛 业务架构师 ​

MySQL 的锁我按作用范围分层,每层都对应真实的业务风险:

  • 全局锁:FTWRL 全库只读, logical 备份时代用过。现在 InnoDB 下用 mysqldump --single-transaction 一致性快照代替,别再让备份锁死交易;
  • 表级锁:表锁本身少用,但两个衍生锁高频出事——MDL 元数据锁:线上 DDL 时,一个未提交的长查询会挡住 DDL,DDL 又挡住后面所有读写,整表请求堆积引发线上事故,所以 DDL 必须低峰期 + 检查长事务;意向锁:IS/IX,声明"表里有行被锁了",让表锁判断不必逐行扫;
  • 行级锁(InnoDB 核心):Record Lock 锁单行;Gap Lock 锁间隙防止插入(解决幻读);Next-Key Lock 前两者组合。间隙锁是高并发死锁的头号来源——两个事务同时持有间隙锁再互相插入,非唯一索引上的范围更新尤其高发;
  • 按思想分:悲观锁(for update,先锁后改,账户资金类首选)与乐观锁(version 字段,CAS 思路,冲突少的场景成本低)。

业务治理三条铁律:事务尽量小(锁持有时间短);更新走索引(否则行锁升级为全表扫描锁全部行);死锁监控告警常态化。锁是并发业务的仲裁器,仲裁规则要架构师来定。

🏗 系统架构师 ​

我从"粒度 + 模式 + 意图"三个维度给 MySQL 锁分类:

按粒度:

  • 全局锁(FTWRL,全库只读,逻辑备份用);
  • 表级锁(表锁、MDL 元数据锁:查自动加 MDL 读锁、DDL 加 MDL 写锁,DDL 被慢查询阻塞会连带阻塞后续所有请求,这是线上 DDL 事故的头号来源)、意向锁(表级标记,让表锁不用逐行检查行锁)、AUTO-INC 锁;
  • 行级锁(InnoDB,锁的是索引记录)。

行锁的三种模式:

  • Record Lock:锁单条索引记录;
  • Gap Lock:锁间隙,RR 级别防插入防幻读;
  • Next-Key Lock:前两者组合,左开右闭区间。

按思想:悲观锁(select ... for update / lock in share mode,先锁后操作)vs 乐观锁(版本号 + CAS 式更新)。

工程要点:行锁加在索引上,条件无索引会升级为全表扫描锁全部记录——这是我评审 SQL 时第一个查的点;锁冲突看 performance_schema.data_locks,死锁看 show engine innodb status。

💻 全栈开发专家 ​

按粒度从大到小:

  • 全局锁:FLUSH TABLES WITH READ LOCK,全库只读,逻辑备份用(InnoDB 一般用 mysqldump --single-transaction 替代)。
  • 表级锁:显式 LOCK TABLES(几乎不用)、MDL 元数据锁(CRUD 自动加 MDL 读锁,DDL 加写锁——线上 DDL 被 slow query 阻塞、继而阻塞后续所有查询的事故源头)、意向锁(表级"有行锁"标记,让表锁判断不用逐行扫)。
  • 行级锁(InnoDB 核心):
    • Record Lock:锁索引记录;
    • Gap Lock:锁区间(RR 级别防插入);
    • Next-Key Lock:记录 + 前面间隙,RR 默认行锁单位。
    • 前提是走索引,条件无索引会升级为锁全表所有记录(实际就是锁全表)。

排查命令:

sql
SELECT * FROM performance_schema.data_locks;          -- 8.0 看锁详情
SELECT * FROM sys.innodb_lock_waits;                  -- 谁堵谁,直接给 KILL 语句

实战提醒:线上 DDL 要看 MDL 等待,用 gh-ost/pt-osc 或 8.0 的 instant DDL;update 没走索引锁全表是"偶发全站卡死"的经典案例,explain 确认索引是第一动作。