MySQL 到底有哪些锁?

前言

应用卡顿?数据库死锁?这些问题的背后,往往藏着 MySQL"锁"这个神秘角色。它既是维持数据秩序的交通警察,也可能是造成拥堵的固执保镖。让我来带你一文看懂全局锁、表级锁与行级锁,让你彻底告别并发烦恼。

全局锁

想象一下,你们公司要年底盘点,老板大手一挥:"都别动!所有货物都不准动,我要看看今年到底赚了多少钱!"

这时候,MySQL 里的 "全局锁" 就登场了。它最霸道,一旦你开启了 "全局锁",整个数据库就变成了"只读"模式

  • 作用场景: 主要就是为了"全库逻辑备份"。你想想,如果不把整个 "仓库" 都锁住,你这边刚数完A货架的货,那边销售又卖出去两个,这账不就乱了吗?所以,必须锁上 "仓库大门",暂停所有"写"操作(增、删、改),让你安安心心地把所有数据复制一份带走。
  • 如何使用: 开启全局锁非常简单,只需要一条命令就可以让 MySQL 变成只读模式。
    • 开启全局锁:FLUSH TABLES WITH READ LOCK
    • 释放锁:UNLOCK TABLES
      • Tips:当你开启全局锁的会话关闭之后也会自动将锁释放。

表级锁

在 MySQL 中表级锁分为:表锁元数据锁意向锁AUTO-INC 锁

表锁

MySQL 的表锁有两种,分别叫读锁 / 共享锁(Table Read Lock)、写锁 / 独占锁(Table Write Lock)。

  • 读锁 (Table Read Lock)
    • 想象一下:在一个餐厅中,服务员对店里的客人说:"大家可以进来参观和阅读菜单(Read ),但不许任何人点菜(Write )!"这个时候很多歌想要"参观" / "阅读"(Read )菜单都可以进入这个包厢;这个时候服务员自己也只能看(Read )不能点菜(Write ),任何想要"点菜"(Write )的客人都会被拦在门外,必须等门锁解除后才能"点菜"(Write)。
    • 这把锁也叫共享锁,当一个会话(Session)给一张表加上了读锁,这个会话自己只能读不能写其他会话也只能读不能写 ,大家都可以共享读取的权限,任何写入操作(INSERT, UPDATE, DELETE)都会被阻塞,直到锁被释放。
    • 如何使用
      • 开启读锁 / 共享锁:LOCK TABLES table_name READ
      • 释放锁:UNLOCK TABLES
  • 写锁(Table Write Lock)
    • 想象一下:在一个餐厅中,厨师对店里的所有人说:"我要修改菜单(Write )了,任何人都不许点菜(Write )!看一眼菜单(Read )都不行!"这个时候,只有这位厨师自己可以在包厢里随意查看修改菜单(Write、Read )。其他任何人,无论是想要看菜单(Read ),还是想要点菜(Write)的人统统都被拦在门外,只能在门口排队等着。
    • 这把锁也叫独占锁,当一个会话给一张表加上了写锁,这个会话自己既可以读也可以写其他任何会话的所有操作(读和写)都会被完全阻塞,直到锁被释放。
    • 如何使用
      • 开启写锁 / 独占锁:LOCK TABLES table_name WRITE
      • 释放锁:UNLOCK TABLES

元数据锁

MySQL 的元数据锁,是 MySQL 非常重要的一个锁机制,元数据指的是"数据的数据",在 MySQL 中指的是数据库对象的定义,比如:表结构、视图、触发器、索引、存储过程......等等。而元数据锁就是用来保护这些元数据的,确保当前线程在数据被访问的时候,不会被其他线程修改。

想象一下:当您在A餐桌点菜吃饭时(执行查询等事务 ),系统会自动给这张桌子加上一个"使用中"的标记,这就是 共享元数据锁 。这个标记不影响其他客人来A桌吃饭(多个查询可并行 ),但它唯一的作用就是告诉老板:桌子有人用,您暂时不能改造或丢掉它(执行ALTER/DROP TABLE等操作)。

但问题也随之而来:如果您这顿饭吃得特别久(长事务未结束),那么想改造桌子的老板就只能一直等待。更糟的是,所有想来A桌的新客人(新的查询请求),看到老板在等,也必须排队,最终导致整个餐厅门口大排长龙,谁也进不来。

MySQL 元数据锁住要目的是为了解决 DML(数据操作语言)和 DDL(数据定义语言)操作之间的冲突问题。当一个事务在使用某个表的元数据时,元数据锁会阻止其他会话对这个表的结构进行修改。MySQL 元数据锁主要分为两种类型:

  • 共享元数据库锁
    • 当执行 DML 操作时(如 SELECT, INSERT, UPDATE, DELETE)会对表加上共享锁。共享锁之间是兼容的,多个事务可以同时持有同一张表的共享 MDL,互不影响,保证了 DML 操作的并发性。
  • 排他元数据锁
    • 当执行 DDL 操作时(如 ALTER TABLE, DROP TABLE, TRUNCATE TABLE)会对表加上排他锁。排他锁与任何其他元数据锁(包括共享锁和排他锁)都是互斥的,这意味着,如果要对一个表执行 DDL,必须等到所有正在使用该表的事务(即使是只读查询)全部结束。反之,如果一个表上有 DDL 操作正在等待或执行,那么其他所有想访问该表的事务(包括 SELECT)都必须等待。

意向锁

MySQL的意向锁(Intention Lock)是一种由 InnoDB 引擎自动管理的 表级锁,但它本身不锁定任何具体的数据行。它的核心作用是一个"信号"或"意向声明",用来协调不同粒度的锁(表锁与行锁)之间可能发生的冲突。

意向锁的出现主要是为了解决性能问题。想象一下,当一个事务想锁定整张表时(如LOCK TABLES),如果没有意向锁,数据库就必须遍历表中的每一行去检查是否存在行锁,这对于大表来说是灾难性的。

有了意向锁,过程就变得高效:当一个事务要给某几行加行锁(如SELECT ... FOR UPDATE)时,它会先在表上加上一个意向排他锁。之后,当另一个事务想锁整张表时,只需检查表上是否存在意向锁,就能立刻知道内部有行锁,从而避免了全表扫描。

AUTO-INC 锁

MySQL的自增锁(AUTO-INC Lock)是一种特殊的表级锁,专用于保护AUTO_INCREMENT自增列,确保在多事务并发INSERT时,自增ID能够唯一、有序地分配,避免冲突。

它的具体行为由innodb_autoinc_lock_mode参数控制,直接影响数据库的并发性能。主要有三种模式:

  1. 传统模式(innodb_autoinc_lock_mode = 0) :在整个INSERT语句执行期间都持有表级的AUTO-INC锁,直到语句结束。这种方式最安全但并发性能最差。
  2. 连续模式(innodb_autoinc_lock_mode = 1) :这是一种混合模式。对于可以预知插入行数的简单INSERT,它使用一个轻量级的锁在内存中快速分配ID后就释放,不阻塞其他事务。但对于INSERT ... SELECT等无法预知行数的批量插入,仍会使用传统的表锁,以保证分配的ID块是连续的。
  3. 交错模式(innodb_autoinc_lock_mode = 2):完全放弃表级锁,全部使用轻量级锁。这种模式下并发性能最好,但代价是单条批量插入语句产生的自增ID可能不再连续。

行锁

在 MySQL 中行锁分为:记录锁(Record Lock)、间隙锁(Gap Lock)、临键锁 (Next-Key Lock)

记录锁

  • 记录锁(Record Lock)顾名思义,就是会锁定单条记录的锁
  • 记录锁有两种实现:共享锁(S 锁)和排他锁(X 锁)
  • 如何加锁
    • 共享锁:
      • 需要手动添加。
        • MySQL 8.0+ 推荐:SELECT ... FOR SHARE;
        • 旧版语法(仍可用):SELECT ... LOCK IN SHARE MODE;
    • 排他锁:
      • 自动加锁 :执行 INSERT, UPDATE, DELETE 操作时,InnoDB 会自动为涉及的行加上X锁。
      • 手动加锁 :执行 SELECT ... FOR UPDATE;

间隙锁

  • 间隙锁(Gap Lock )不锁定任何记录本身,它锁定的是索引记录之间的"间隙 ",其设计的核心目的是为了在 可重复读(Repeatable Read)隔离级别下,防止幻读 的发生。
  • 如何加锁
    • 使用范围查询 (如 BETWEEN, >, <)或者查询一个不存在的记录时,会触发间隙锁。

临键锁

  • 临键锁 (Next-Key Lock)记录锁 + 间隙锁 的组合,它会锁定一个索引记录本身,以及该记录之前的那个间隙。
  • 临键锁是 InnoDB 在可重复读(Repeatable Read)隔离级别下的默认锁定算法。它既能防止数据被修改,又能防止幻读。
  • 如何加锁
    • 表中有 id 为 10 和 20 的记录,SELECT * FROM users WHERE id < 15 FOR UPDATE; 会锁定 id=10 的记录(记录锁)以及 id=10 之前的间隙 (-∞, 10](间隙锁),组合成一个临键锁。这样,其他事务既不能修改 id=10 的记录,也不能在 (-∞, 10) 这个区间插入新数据。
相关推荐
IManiy2 分钟前
总结之Vibe Coding:后端骨架
后端
ikoala4 分钟前
Codex 怎么买、怎么充值?先把这两套计费搞清楚
前端·javascript·后端
前端Hardy34 分钟前
一个时代结束了:npm 终于对 install 脚本下手了
前端·javascript·后端
damaoyou36 分钟前
Cog3DRangeImagePlaneEstimatorTool完全指南
后端
Nturmoils1 小时前
分页别写太顺手,LIMIT 背后还有排序和边界
数据库·后端
神奇小汤圆1 小时前
国产版“Codex”初体验,智谱ZCode很强啊!
后端
站大爷IP1 小时前
Python里的“赋值”到底是什么意思?
后端
鹅城剑仙2 小时前
Spring Boot 微服务架构设计与最佳实践
spring boot·后端·微服务
Full Stack Developme3 小时前
Spring Integration 教程
java·后端·spring
爱勇宝3 小时前
AI 时代,前端工程师的话语权正在下降?
前端·后端