遊戲 Lag 白皮書 › L12 資料庫
熱點資料列鎖定競爭 Hot row lock contention
原因 ID db-hot-row · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
在含圖解與實驗的完整版中開啟卡片 →
所有人都要修改同一筆資料列(公會倉庫、拍賣場熱門道具、全伺服器共用的計數器)時,一次只有一個請求能取得鎖定。
為什麼 活動、熱門道具讓修改集中在同一筆資料列 → 於是 請求一直等到取得鎖定為止 → 畫面上 交易失敗、「請稍後再試」、逾時
- 症狀
- 吃指令/回檔, 輸入延遲
- 因素
- 停滯, 延遲
- 誰會遇到
- 只有特定功能
- 何時
- 人潮湧入時
- 負責單位
- 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(DB 基礎設施)
- 遊戲開發團隊要做的事
- 拆分資料列(分片計數器,sharded counter)、縮短 transaction、先在記憶體中彙總再一次寫入。
- 基礎設施團隊要做的事
- 監控資料列鎖定的等待時間與次數,找出競爭集中的資料列並分享結果。
- 數值參考
- 一個請求持有鎖定 10ms 的話,這筆資料列每秒最多只能修改 100 次。transaction 中若夾著與其他伺服器的往返,次數還會再減少。
- 圖表上
- 隨人數/負載上升 · 資料列鎖定等待次數與時間
- 查看位置
- 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 的 session、pg_locks 中 granted 為 false 的請求;開啟 log_lock_waits(預設關閉)後,等待很久的鎖定會記錄在 log 中
- 符合的跡象
- 鎖定等待隨活動、人數急遽增加,等待中的請求大多指向同一張資料表的同一筆資料列(同一個 key)
- 不符合的跡象
- 等待平均分散在多張資料表、多筆資料列時,是磁碟或 CPU 飽和的問題。單一 session 長時間持有鎖定不放時,是長時間未結束的 transaction(db-long-tx)
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
出處
- InnoDB Locking MySQL
一個 transaction 鎖定資料列(索引記錄)後,其他 transaction 無法修改該資料列,只能等待 - How to Minimize and Handle Deadlocks MySQL
建議讓 transaction 保持小而短,相關變更完成後立刻 commit,以減少衝突 - 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)、造成阻擋的 session(blocking_pid)、等待時間(wait_age) - The Cumulative Statistics System (PostgreSQL Documentation) PostgreSQL
pg_stat_activity 的 wait_event_type:為 Lock 時表示正在等待重量級鎖(heavyweight lock) - pg_locks (PostgreSQL Documentation) PostgreSQL
granted 為 false 時,表示該處理程序正在等待取得鎖定 - Error Reporting and Logging (PostgreSQL Documentation) PostgreSQL
log_lock_waits:等待鎖定的時間超過 deadlock_timeout 時寫入 log,預設關閉
相關原因
同一層:L12 資料庫
同一症狀(吃指令/回檔)在其他層的原因
查看含圖解與實驗的完整版卡片