ゲームラグ白書 › L12 データベース
ホットスポットの行ロック競合 Hot row lock contention
原因ID db-hot-row · 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・DBインフラ
図と実験のあるメインページでこのカードを開く →
全員が同じ行(ギルド倉庫、オークションの人気アイテム、サーバー全体のカウンター)を更新しようとすると、ロックを取れるのは1人ずつです。
なぜ イベント・人気アイテムで同じ行に更新が集中 → すると ロックを取れるまで要求が待たされる → 画面では 取引失敗、「しばらくしてからもう一度お試しください」、タイムアウト
- 症状
- 不発・ロールバック, 入力遅延
- 要因
- ストール, 遅延
- 誰に起きるか
- 特定の機能だけ
- いつ
- 人が集中したとき
- 担当
- 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・DBインフラ
- ゲーム開発チームの対応
- 行の分割(シャーディングしたカウンター)、トランザクションを短く、メモリで集約して一度に反映。
- インフラチームの対応
- 行ロックの待ち時間・件数を監視し、競合が集中する行を見つけて共有。
- 数値の目安
- 1つの要求がロックを10ms保持していると、その行は1秒に最大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の飽和側。1つのセッションがロックを長く保持して離さないなら、長時間開いたままのトランザクション(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 データベース
同じ症状(不発・ロールバック)を起こすほかの層の原因
図と実験のあるメインページでこのカードを見る