元数据与表锁

共 19 题
📑 题目列表 19 题
#
★★★

1. MySQL InnoDB 表锁,IS、IX、S、X 模式与意向锁?

MySQL InnoDB 的表锁有哪些模式?IS、IX、S、X 与意向锁的作用与关系是什么?

  • 表级锁的四种模式
  • 意向锁的概念
  • 表锁与行锁的协同

InnoDB 的表级锁包括四种模式:共享锁(S)、排他锁(X)、意向共享锁(IS)、意向排他锁(IX)。其中 S/X 是表级读写锁,IS/IX 是意向锁,它表示"该事务打算在表内加行级共享/排他锁"。意向锁本身不直接锁数据,而是用于表示加行锁的意图,使表锁与行锁能够兼容判断:例如事务要加表级 S 锁时,需检查是否已有事务持有 IX 锁(表示有行级排他锁),避免冲突。意向锁之间(IS/IX)相互兼容,S 与 X、S 与 IX、X 与其他都冲突。

意向锁是"表锁与行锁协同"的桥梁,它让数据库能快速判断"表级锁是否与已有的行级锁冲突",而无需逐行检查。理解意向锁协议是理解 InnoDB 表锁与行锁协同的基础。

#
★★★

2. MySQL 中 metadata lock 等待链,长事务导致 ALTER TABLE 卡住?

MySQL 中的 metadata lock(MDL)等待链是什么?为何长事务会导致 ALTER TABLE 卡住?

  • MDL 的作用
  • 长事务持有 MDL 读锁
  • ALTER TABLE 等 DDL 被阻塞

MySQL 的 metadata lock(元数据锁)用于保护表结构定义,防止 DML 与 DDL 并发时结构不一致。所有访问表的事务(SELECT/UPDATE 等)都会持有表的 MDL 共享锁,DDL(如 ALTER TABLE)需要 MDL 排他锁。若某个长事务持有了 MDL 共享锁迟迟不释放,ALTER TABLE 请求的 MDL 排他锁就得一直等待,导致 ALTER TABLE 卡住(表现为"Waiting for table metadata lock")。更严重的是,后续所有新语句也会排在 ALTER 后面形成等待链,阻塞整个表的所有访问。

MDL 等待链是生产环境常见问题,根源是长事务占用 MDL 读锁。解决方法是定位并结束长事务,或使用 MySQL 8.0 的 ALTER TABLE ... INSTANT 等在线 DDL 减少阻塞。

#
★★★

3. PostgreSQL 中 advisory lock(咨询锁)的应用?

PostgreSQL 中的 advisory lock(咨询锁)是什么?它有哪些典型应用?

  • advisory lock 的概念
  • 与应用层协调
  • 典型应用场景

PostgreSQL 的 advisory lock(咨询锁)是一种由应用自行约定语义的锁,数据库不负责解释其含义,只负责提供加锁/解锁机制。它不是数据库行锁或表锁,而是供应用在多进程间协调共享资源(如定时任务互斥、缓存重建、分布式调度)的锁。典型应用包括:保证定时任务只在一个实例执行(pg_try_advisory_lock 抢锁)、库存预占、防止并发执行同一逻辑等。advisory lock 有会话级和事务级两种,可通过 session/transaction 作用域控制释放时机。

advisory lock 的价值在于"通用协调原语",它把数据库当作一个分布式锁服务。相比数据库行锁,advisory lock 不绑定具体数据,灵活但需应用自律,避免锁泄漏。

#
★★★

4. PostgreSQL 中表级锁,ACCESS EXCLUSIVE、SHARE、EXCLUSIVE 模式?

PostgreSQL 的表级锁有哪些模式?ACCESS EXCLUSIVE、SHARE、EXCLUSIVE 分别表示什么?

  • 表级锁模式
  • ACCESS EXCLUSIVE 最严格
  • SHARE/EXCLUSIVE 的用途

PostgreSQL 的表级锁从弱到强有:ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE。其中 ACCESS EXCLUSIVE 是最严格的锁,与所有其他锁冲突,用于 VACUUM FULL、ALTER TABLE、DROP TABLE 等操作,会阻塞一切读写。SHARE 锁用于需要保证表结构稳定且阻止数据写入的场景(如 CREATE INDEX 在某些情况下)。EXCLUSIVE 锁介于两者之间,与 ROW EXCLUSIVE 等冲突。不同操作会自动获取相应强度的表锁(如 SELECT 取 ACCESS SHARE,UPDATE 取 ROW EXCLUSIVE)。

PostgreSQL 的表锁矩阵用于描述不同操作之间的兼容性。理解锁强度与冲突关系,是排查 PostgreSQL 表锁阻塞(如 DDL 与查询互等)的基础。

#
★★★

5. 元数据锁(Metadata Lock),DDL 与 DML 之间的锁协调?

元数据锁(Metadata Lock)是什么?它如何协调 DDL 与 DML?

  • MDL 的概念
  • DML 持共享锁、DDL 持排他锁
  • 协调机制

元数据锁(Metadata Lock,MDL)用于保护表结构(schema)不被并发修改,保证 DML 在执行期间表结构稳定。MySQL 中,DML(SELECT/INSERT/UPDATE/DELETE)会获取表的 MDL 共享锁,DDL(ALTER TABLE/DROP TABLE)需要 MDL 排他锁。共享锁与排他锁冲突,因此 DDL 必须等待所有 DML 释放 MDL 共享锁后才能执行,而 DML 也会在 DDL 之后排队等待。这种协调保证了"执行语句时结构不变""结构变更时无并发语句"。PostgreSQL 通过表级锁系统(如 ACCESS EXCLUSIVE)实现类似语义。

MDL 是 DDL 与 DML 之间的一致性与并发协调机制。它在保证结构安全的同时,也带来了 DDL 被长事务阻塞的问题,需要在"结构安全"与"并发"之间取舍。

#
★★★

6. 表锁(Table Lock)的语义,锁定整张表的所有操作?

表锁(Table Lock)的语义是什么?它如何锁定整张表的所有操作?

  • 表锁的概念
  • 锁定整张表
  • 与行锁的对比

表锁(Table Lock)是锁定整张表的锁,一旦获取,其他事务对该表的相关操作(读写)会被阻塞,直到锁释放。表锁粒度大、开销小,但并发度低。MyISAM 使用表锁(读写互斥),InnoDB 默认使用行锁,但表级锁(S/X 及意向锁)仍存在,用于配合行锁或显式 LOCK TABLE 时使用。表锁的语义是"整表级别互斥",适合低并发或全表操作场景,但会显著降低高并发下的吞吐。

表锁与行锁的根本差异是"锁粒度"。表锁粒度大、并发差、开销小;行锁粒度小、并发高、开销大。理解两者取舍是选择存储引擎与加锁策略的基础。

#
★★★

7. 行锁与表锁的协同,InnoDB 的意向锁协议?

行锁与表锁如何协同?InnoDB 的意向锁协议是怎样的?

  • 意向锁的作用
  • 加行锁前先加意向锁
  • 表锁与行锁兼容判断

InnoDB 通过意向锁协议实现行锁与表锁的协同。事务在对某行加行级共享锁(S)前,必须先对该表加意向共享锁(IS);加行级排他锁(X)前,必须先加意向排他锁(IX)。意向锁不锁数据,只表示"该表内有行级锁"的意图。这样,当某个事务想加表级锁(如 S 或 X)时,只需检查表中是否已有冲突的意向锁(如已有 IX 则表级 S 不兼容),无需逐行扫描判断,从而高效实现表锁与行锁的兼容性检查。

意向锁协议是"以意向锁为索引、快速判断表锁与行锁冲突"的机制。它让表锁能感知表内行锁的存在,兼顾表级操作的原子性与行级操作的并发。

#
★★★

8. PostgreSQL 的 DDL 具备事务性(可回滚)而 MySQL 的 DDL 会隐式提交,这一差异对 Online DDL 失败恢复与主从复制分别意味着什么?

PostgreSQL 与 MySQL 的 DDL 在事务性上有何差异?这对 Online DDL 失败恢复与主从复制分别意味着什么?

  • PostgreSQL DDL 可回滚 vs MySQL DDL 隐式提交
  • 对失败恢复的影响
  • 对主从复制的影响

PostgreSQL 的 DDL 是事务性的,可以放在事务中执行、失败可整体回滚,DDL 与 DML 在一致性和原子性上同等对待。MySQL 的 DDL(如 ALTER TABLE)会隐式提交,不能在事务中回滚,一旦执行会立即生效,中途失败会留下部分完成的结构变更。对 Online DDL 失败恢复而言,PostgreSQL 可按需回滚、原子性更好;MySQL 则需人工修复可能导致的结构不一致。对主从复制而言,PostgreSQL 的 DDL 作为事务的一部分复制,从库重放时同样具备原子性;MySQL 的 DDL 在从库重放时同样需要获取元数据锁,但无法回滚,若从库长查询阻塞重放会拖慢复制。

DDL 事务性是 PostgreSQL 与 MySQL 的重要架构差异。PostgreSQL 的原子 DDL 带来更强的失败恢复能力,而 MySQL 的在线 DDL 虽降低阻塞但牺牲了可回滚性,两者取舍不同。

#
★★★

9. 主库执行 DDL 后在从库重放时同样需要获取元数据锁,为什么从库上的长查询会阻塞 DDL 重放并拖慢复制?如何缓解?

从库上的长查询为何会阻塞 DDL 重放并拖慢复制?如何缓解?

  • 从库重放 DDL 需获取元数据锁
  • 长查询阻塞 DDL 重放
  • 复制延迟与缓解

主库执行 DDL 后,对应的 DDL 语句会通过复制传到从库,从库在重放时同样需要获取该表的元数据锁(MySQL 的 MDL 排他锁或 PostgreSQL 的表排他锁)。如果从库上有正在运行的针对该表的长查询,它持有 MDL 共享锁,从库的 DDL 重放就无法获取排他锁,导致 DDL 重放被阻塞,进而阻塞其后所有复制事件,造成复制延迟(从库落后主库)。缓解方法包括:在从库设置 lock_timeout 让 DDL 重放超时重试、规划从库的只读查询与 DDL 时段、用分区滚动维护避免大表长 DDL、以及在从库上避免长事务。

从库 DDL 重放被长查询阻塞是复制延迟的常见原因。理解"从库重放同样需要元数据锁"这一机制,就能针对性地通过锁超时、查询规划等手段缓解。

#
★★

10. MySQL 的 MDL(Metadata Lock)类型(SHARED、EXCLUSIVE 等)与 Online DDL 分阶段降级机制,为什么 ADD COLUMN 可以做到不阻塞 DML?

MySQL 的 MDL 类型有哪些?Online DDL 如何分阶段降级?为什么 ADD COLUMN 可以不阻塞 DML?

  • MDL 类型(SHARED/EXCLUSIVE)
  • Online DDL 分阶段
  • ADD COLUMN 不阻塞 DML 的原因

MySQL 的 MDL 有共享锁(SHARED,S 锁,DML 持有)和排他锁(EXCLUSIVE,X 锁,DDL 需要)等类型。Online DDL 在执行时分多个阶段,通过"降级"机制释放过强的锁:在构建阶段(如 ADD COLUMN 做 in-place 变更)只短暂持有较弱的锁(如 SHARED_UPGRADEABLE),大部分时间与 DML 兼容,只有最后的 commit 阶段才短暂获取排他锁。因此 ADD COLUMN 这类 in-place 操作可以在不阻塞 DML 的情况下执行,因为真正的数据变更在后台完成,不占用排他锁。而某些操作(如需要重建表且无法 in-place)的 Online DDL 才会在特定阶段阻塞写。

Online DDL 的核心是"分阶段 + 锁降级",把需要排他锁的时间窗口压缩到几乎为零。理解锁降级机制就能解释为什么大多数 Online DDL 不影响并发 DML。

#
★★

11. PostgreSQL 表锁强度矩阵(ACCESS SHARE → ACCESS EXCLUSIVE)中哪些组合互相冲突?SELECT 与 VACUUM、ALTER TABLE 的兼容性如何?

PostgreSQL 表锁强度矩阵中哪些组合互相冲突?SELECT 与 VACUUM、ALTER TABLE 的兼容性如何?

  • 表锁强度矩阵
  • SELECT 取 ACCESS SHARE
  • 与 VACUUM、ALTER TABLE 的兼容性

PostgreSQL 的表锁矩阵中,ACCESS EXCLUSIVE 与所有锁冲突;EXCLUSIVE 与 ROW EXCLUSIVE、SHARE ROW EXCLUSIVE、SHARE、EXCLUSIVE、ACCESS EXCLUSIVE 冲突;SHARE 与 ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE ROW EXCLUSIVE、SHARE、EXCLUSIVE、ACCESS EXCLUSIVE 冲突。SELECT 获取 ACCESS SHARE 锁,VACUUM 获取 SHARE UPDATE EXCLUSIVE 锁,两者兼容(ACCESS SHARE 与 SHARE UPDATE EXCLUSIVE 不冲突),因此 SELECT 与 VACUUM 可并发。ALTER TABLE(如 VACUUM FULL 或大部分 ALTER)获取 ACCESS EXCLUSIVE 锁,与 SELECT 的 ACCESS SHARE 冲突,因此 ALTER TABLE 会阻塞 SELECT,反之亦然。

表锁矩阵是 PostgreSQL 判断操作并发性的规则。理解 SELECT 与 VACUUM 兼容、与 ALTER TABLE 冲突,是分析表锁阻塞的基础。

#
★★

12. 为什么 autovacuum 会与 DDL 或长查询互相阻塞?如何用 lock_timeout、分区滚动维护与 maintenance_work_mem 缓解?

为什么 autovacuum 会与 DDL 或长查询互相阻塞?如何缓解?

  • autovacuum 的表锁
  • 与 DDL/长查询的阻塞
  • lock_timeout、分区滚动、maintenance_work_mem

autovacuum 会对表获取 SHARE UPDATE EXCLUSIVE 锁,该锁与某些 DDL 操作(如 ALTER TABLE 需要 ACCESS EXCLUSIVE)冲突,也与某些长查询(如持有 ACCESS SHARE 但需要 SHARE UPDATE EXCLUSIVE 的维护操作)冲突,从而互相阻塞。缓解方法:设置 lock_timeout 让 DDL 或维护操作在锁等待超时后放弃而非无限等待;采用分区滚动维护,每次只维护一个分区,缩短单次锁持有时间;调整 maintenance_work_mem 提升 VACUUM 等维护操作的效率,缩短维护耗时。此外可关闭某个表的 autovacuum 或调整其触发参数。

autovacuum 与维护操作之间的锁冲突是 PostgreSQL 运维的常见问题。核心思路是"缩短锁持有时间"与"避免无限等待",通过 lock_timeout、分区策略、维护参数来缓解。

#
★★

13. MyISAM 表锁与 InnoDB 行锁在并发读写、读写互斥与崩溃恢复上的历史差异,为什么现代系统普遍弃用 MyISAM?

MyISAM 表锁与 InnoDB 行锁在并发读写、读写互斥与崩溃恢复上有何差异?为什么现代系统普遍弃用 MyISAM?

  • MyISAM 表锁、读写互斥
  • InnoDB 行锁、读写并发
  • 崩溃恢复差异

MyISAM 使用表级锁,读写操作互斥(写锁会阻塞所有读,读锁会阻塞写,且不支持行级并发),并发能力差;不提供崩溃恢复能力(崩溃后可能损坏,需修复)。InnoDB 使用行级锁,支持读写并发(MVCC 下读不阻塞写),且通过 redo/undo log 提供崩溃恢复能力。现代系统普遍弃用 MyISAM,是因为在线业务通常需要高并发读写与可靠性,MyISAM 的表锁与缺乏崩溃恢复无法满足。MySQL 8.0 已移除 MyISAM 的数据字典支持,推荐使用 InnoDB。

MyISAM 与 InnoDB 的核心差异是"锁粒度与可靠性"。MyISAM 表锁 + 无崩溃恢复,适合低并发只读场景;InnoDB 行锁 + MVCC + 崩溃恢复,适合高并发事务场景。这也是选择存储引擎的关键考量。

#
★★

14. LOCK TABLE 的语法与锁模式,ACCESS SHARE 到 ACCESS EXCLUSIVE 与 DDL/查询的互斥关系,什么场景应显式加表锁而非依赖行锁?

LOCK TABLE 的语法与锁模式是什么?什么场景应显式加表锁而非依赖行锁?

  • LOCK TABLE 语法
  • 锁模式 ACCESS SHARE 到 ACCESS EXCLUSIVE
  • 显式加表锁的场景

PostgreSQL 的 LOCK TABLE 语法为 LOCK TABLE 表名 IN 锁模式 MODE,锁模式从 ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 到 ACCESS EXCLUSIVE,强度递增,与 DDL/查询的互斥关系由锁矩阵决定。MySQL 的 LOCK TABLES 可显式加 READ/WRITE 表锁。显式加表锁的场景包括:需要锁定多张表以保持跨表一致性(如批量迁移)、确保某表在操作期间完全不被读写(如导入数据)、MyISAM 表强制表锁等。当单靠行锁无法保证跨表或全表一致性时,才显式加表锁。

LOCK TABLE 是"显式、表级"的加锁手段,比行锁粒度大。它适合需要全表或跨表一致性的场景,但会降低并发,应谨慎使用。

#
★★

15. 外键约束的增删为何需要独占元数据锁?Inplace 校验外键期间与 DML 的并发边界在哪里?

外键约束的增删为何需要独占元数据锁?Inplace 校验外键期间与 DML 的并发边界在哪里?

  • 外键增删需独占 MDL
  • Inplace 校验外键
  • 与 DML 的并发边界

增删外键约束会改变表的引用关系定义,需要独占元数据锁来保证结构变更期间没有并发 DML 干扰,否则无法保证一致性。MySQL 中,ADD FOREIGN KEY 等操作在 Inplace 校验外键数据时,会先获取排他 MDL 阻止写入,校验完数据后转为共享锁,期间与 DML 的并发边界是:校验阶段 DML 被阻塞,校验完成后 DML 恢复,但结构变更的元数据仍受保护。因此外键增删的锁持有时间取决于校验外键数据的耗时,数据量大时阻塞时间较长。

外键校验需要"先独占读数据、再恢复 DML",其并发边界由校验阶段决定。理解这一点能解释外键 DDL 的阻塞窗口与优化空间。

#

16. MySQL 的 LOCK TABLES/UNLOCK TABLES 语句何时仍有用(批量维护、MyISAM 迁移)?它与行锁、MDL 的叠加关系?

MySQL 的 LOCK TABLES/UNLOCK TABLES 语句何时仍有用?它与行锁、MDL 的叠加关系是什么?

  • LOCK TABLES 的用途
  • 批量维护、MyISAM 迁移
  • 与行锁、MDL 的叠加

LOCK TABLES 在批量维护、需要保证多表一致快照、MyISAM 迁移等场景仍有用。例如批量更新多张表时,用 LOCK TABLES 锁定这些表以避免并发干扰;迁移 MyISAM 表时用表锁保证一致性。它获取的是表级锁,与 InnoDB 行锁是不同粒度:行锁锁行,表锁锁整表。LOCK TABLES 会一并获取表的 MDL,且与 MDL 叠加——显式表锁与 DDL 的 MDL 排他锁冲突。因此 LOCK TABLES 会阻塞其他事务对该表的读写,需谨慎使用并及时 UNLOCK TABLES。

LOCK TABLES 是显式表锁,与行锁、MDL 形成"表锁→行锁→MDL"的多层结构。它适用于低并发的一致性维护,但会牺牲并发,需权衡。

#

17. MySQL 表锁与 DDL 的关系,Online DDL 如何减少锁表影响?

MySQL 表锁与 DDL 是什么关系?Online DDL 如何减少锁表影响?

  • 表锁与 DDL 的关系
  • Online DDL 减少锁表
  • 分阶段降级

MySQL 的 DDL 需要获取表的 MDL 排他锁,传统 DDL 会全程锁表,阻塞并发 DML。Online DDL(如 ALTER TABLE ... ALGORITHM=INPLACE)通过分阶段执行和锁降级,减少锁表影响:构建阶段使用较弱的锁(如 SHARED_UPGRADEABLE)与 DML 兼容,仅在最后 commit 阶段短暂加排他锁,从而把阻塞时间降到最低。因此 Online DDL 能让 ADD COLUMN 等操作在表在线的情况下执行,不阻塞写入。相比传统 DDL 的 COPY 算法(锁表),Online DDL 显著减少了锁表窗口。

Online DDL 的意义在于"减少锁表时间",通过部分 COPY 或 INPLACE 算法与锁降级,把排他锁窗口压缩到几乎为零,是 MySQL 5.6+ 在线变更的核心能力。

#

18. 表锁 vs 行锁,MyISAM 与 InnoDB 的锁粒度差异?

表锁与行锁的锁粒度有何差异?MyISAM 与 InnoDB 各采用哪种?

  • 表锁锁粒度
  • MyISAM 表锁、InnoDB 行锁
  • 并发与开销差异

表锁锁定整张表,粒度大,并发度低但开销小;行锁锁定单行记录,粒度小,并发度高但开销大(需维护锁记录)。MyISAM 只支持表锁,读写操作互斥,并发能力差;InnoDB 支持行锁(配合 MVCC 和意向锁),读写可并发,适合高并发场景。锁粒度差异直接影响并发吞吐与锁竞争:表锁下并发写会互相阻塞,行锁下不同行的操作可并行。这也是 InnoDB 取代 MyISAM 成为默认存储引擎的重要原因。

锁粒度是存储引擎并发模型的核心。表锁适合低并发、全表操作;行锁适合高并发、随机访问。理解二者差异是存储引擎选型的基础。

#

19. 崩溃恢复后数据库为什么不会残留任何锁?锁的持有与释放如何与事务提交或回滚绑定?

崩溃恢复后数据库为什么不会残留任何锁?锁的持有与释放如何与事务提交或回滚绑定?

  • 锁随事务生命周期
  • 崩溃后锁清理
  • 锁与事务绑定

锁的持有与释放是绑定在事务生命周期上的:事务获取的锁在事务提交或回滚时释放,无论正常结束还是异常中断。数据库崩溃后,所有未完成的事务都会被回滚(崩溃恢复流程),它们持有的锁也随之释放,恢复完成后内存锁表被清空重建,因此不会残留任何锁。这是因为锁是内存中的临时结构,不持久化,崩溃恢复后重新加载数据并回滚未提交事务,锁自然消失。这也保证了恢复后数据库处于一致、无锁阻塞的状态。

锁不持久化、不与数据一起落盘,而是随事务生灭。崩溃恢复通过回滚未提交事务来释放锁,这一设计保证了恢复后无锁残留。理解这一点能解释为什么数据库重启后不会出现"锁死"。