游戏卡顿白皮书 › L12 数据库
热点行锁竞争 Hot row lock contention
原因 ID db-hot-row · 主责 研发团队·服务器开发 · 配合 运维团队·数据库运维
在含图示和实验的完整版中打开此卡片 →
所有人都要修改同一行(公会仓库、拍卖行热门物品、全服计数器)时,同一时刻只有一个人能拿到锁。
起因 活动、热门物品让修改集中到同一行 → 结果 请求一直等到拿到锁 → 画面表现 交易失败、“请稍后再试”、超时
- 症状
- 吞操作/回档, 操作延迟
- 因素
- 停顿, 延迟
- 谁会遇到
- 仅特定功能
- 何时出现
- 人多的时候
- 负责方
- 主责 研发团队·服务器开发 · 配合 运维团队·数据库运维
- 研发团队要做的事
- 拆分行(分片计数器),缩短事务,在内存中汇总后一次性写入。
- 运维团队要做的事
- 监控行锁等待时间和次数,找出竞争集中的行并共享结果。
- 数值参考
- 一个请求持锁 10 ms,这一行每秒最多只能修改 100 次。如果事务中还夹着与其他服务器的往返通信,能修改的次数会更少。
- 监控图上
- 随人数/负载上升 · 行锁等待次数、时长
- 查看位置
- MySQL 看 Innodb_row_lock_waits、Innodb_row_lock_time 的增量和 Innodb_row_lock_current_waits,用 sys.innodb_lock_waits 找出谁在等谁。PostgreSQL 看 pg_stat_activity 中 wait_event_type 为 Lock 的会话、pg_locks 中 granted 为 false 的请求;开启 log_lock_waits(默认关闭)后,等待时间长的锁会记入日志
- 确认依据
- 锁等待随活动、人数陡增,等待中的请求大多指向同一张表的同一行(同一个键)
- 排除依据
- 等待均匀分散在多张表、多行上,属于磁盘、CPU 饱和问题。某个会话长时间持锁不放,看“长事务”(db-long-tx)
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- InnoDB Locking MySQL
一个事务给行(索引记录)加锁后,其他事务无法修改这一行,只能等待 - How to Minimize and Handle Deadlocks MySQL
建议事务保持小而短,相关修改完成后立即提交,以减少冲突 - Server Status Variables MySQL
用 Innodb_row_lock_waits、Innodb_row_lock_time 查看行锁等待次数和时间,用 Innodb_row_lock_current_waits 查看当前正在等待的数量 - The innodb_lock_waits and x$innodb_lock_waits Views MySQL
等待中的查询(waiting_query)、阻塞它的会话(blocking_pid)和等待时长(wait_age) - The Cumulative Statistics System (PostgreSQL Documentation) PostgreSQL
pg_stat_activity 的 wait_event_type:为 Lock 表示正在等待重量级锁 - pg_locks (PostgreSQL Documentation) PostgreSQL
granted 为 false 表示该进程正在等待获取锁 - Error Reporting and Logging (PostgreSQL Documentation) PostgreSQL
log_lock_waits:锁等待超过 deadlock_timeout 时记录日志,默认关闭
相关原因
同一层:L12 数据库
其他层中同样导致“吞操作/回档”的原因
查看含图示和实验的原卡片