游戏卡顿白皮书 › L12 数据库
线上表结构变更(DDL)锁 Schema change lock (DDL / metadata lock)
原因 ID db-ddl-lock · 主责 运维团队·数据库运维 · 配合 研发团队·服务器开发
在含图示和实验的完整版中打开此卡片 →
在服务运行中给表加列或加索引,仅仅因为一个短暂需要的锁,使用这张表的所有请求都可能被迫等待。
起因 通过热修复给线上表加列、加索引 → 结果 表结构变更在等之前开启的长事务,后来的所有请求又在等这个表结构变更 → 画面表现 使用这张表的功能(背包、邮件等)整体卡住并超时
- 症状
- 操作延迟, 吞操作/回档, 连不上/无限加载
- 因素
- 停顿
- 谁会遇到
- 仅特定功能, 全服
- 何时出现
- 偶尔随机, 刚登录/维护结束后
- 负责方
- 主责 运维团队·数据库运维 · 配合 研发团队·服务器开发
- 研发团队要做的事
- 含表结构变更的热修复要与数据库运维协商时间,先发布在没有新列时也能正常运行的代码。
- 运维团队要做的事
- 设置较短的锁等待超时,失败就重试;在没有长事务时执行;使用在线变更工具;大表放到维护时间做。
- 监控图上
- 某一时刻起台阶式上升 · 锁等待会话数、这张表的查询延迟
- 查看位置
- MySQL 统计 SHOW PROCESSLIST 中 State 为 Waiting for table metadata lock 的会话,用 sys.schema_table_lock_waits 找出阻塞它们的会话(blocking_pid)。PostgreSQL 看 pg_locks 中 granted 为 false 的请求和 AccessExclusiveLock,用 pg_blocking_pids() 找出阻塞它们的会话
- 确认依据
- 从开始变更表结构的时刻起,使用这张表的所有查询都堆积在锁等待中,排在最前面的是未结束的事务或表结构变更语句
- 排除依据
- 等待只集中在特定行上,同一张表的其他行处理正常,看“热点行锁竞争”(db-hot-row)
- 确认手段
- 运维工具即可确认(无需游戏代码)
- 深入了解
- 变更表结构时,MySQL 会短暂持有元数据锁,PostgreSQL 会短暂持有最强的表锁。变更本身哪怕一瞬间就完成,只要前面有一个未结束的事务,后面的所有请求都会排队等待。
出处
- Online DDL Performance and Concurrency MySQL
在线 DDL 在收尾时也需要短暂持有排他元数据锁;有长事务时会等待,而这个等待中的锁请求会阻塞后面所有事务 - Server System Variables MySQL
lock_wait_timeout:元数据锁等待超时,默认值 31,536,000 秒(1 年) - ALTER TABLE (PostgreSQL Documentation) PostgreSQL
未特别注明的 ALTER TABLE 会获取最强的 ACCESS EXCLUSIVE 锁 - Client Connection Defaults (PostgreSQL Documentation) PostgreSQL
lock_timeout:锁等待超过这个时长就中止语句 - General Thread States MySQL
Waiting for table metadata lock:正在等待元数据锁的线程状态 - The schema_table_lock_waits and x$schema_table_lock_waits Views MySQL
等待元数据锁的会话(waiting_query)和阻塞它的会话(blocking_pid) - pg_locks (PostgreSQL Documentation) PostgreSQL
granted 为 false 表示正在等待锁,mode 显示 AccessExclusiveLock 等锁类型 - System Information Functions and Operators (PostgreSQL Documentation) PostgreSQL
pg_blocking_pids():阻止指定会话获得锁的会话列表
相关原因
同一层:L12 数据库
其他层中同样导致“操作延迟”的原因
查看含图示和实验的原卡片