MySql Lock wait timeout exceeded 小記
今天在 log 發現少數 SQL 會出現 Lock wait timeout exceeded。這類問題第一次看到很容易直覺想把 timeout 調大,但實務上大多不是等不夠久,而是有交易拿著鎖太久、查詢路徑不對,或流程本身設計就容易互卡。
這個錯誤代表什麼
簡單說,就是你的交易想取得某個鎖,但一直等不到,等到超過 innodb_lock_wait_timeout 就放棄了。
這和 deadlock 不完全一樣:
- deadlock 是彼此互鎖,MySQL 會主動挑一個回滾。
- lock wait timeout 是單方面一直等,等太久後才失敗。
最常見原因
我後來整理,常見情況大概是這幾種:
- 交易開太久,中間還做很多非必要邏輯。
- 更新條件沒有吃到索引,鎖範圍比想像中大。
- 高併發下多個請求搶同一批資料。
insert into ... select這類語句影響範圍太廣。- 應用程式忘記及時 commit 或 rollback。
不建議第一步就調大 timeout
很多文章都會提到 innodb_lock_wait_timeout,但我自己也認同一個觀點:不要先把 timeout 拉很長。
mysql transaction default timeout is 50 sec => my suggestion is between 3 ~ 5 sec
我同事當時也建議不要設太長。因為如果真正問題是鎖競爭或慢 SQL,拉長等待時間只是讓更多 request 堆在那邊,後果可能更糟。
我會怎麼排查
1. 先找誰在持有鎖
先看目前有哪些長交易、哪些 session 正在等鎖。重點不是只看失敗那條 SQL,而是找前面那個一直不放手的人。
2. 看 SQL 是否有吃到索引
如果更新條件掃太大範圍,InnoDB 就可能鎖到更多 row,導致別人也進不去。
3. 檢查交易範圍
有些程式會先查資料、呼叫外部 API、寫 log、最後才 commit。這種流程只要一慢,鎖就跟著拖很久。
4. 回頭看業務是否需要冪等或排隊
如果很多請求本來就在搶同一筆資料,問題可能不是 SQL 調一調就能解,而是流程要重新設計,例如序列化處理、重試機制、樂觀鎖等。
我自己的處理觀念
- 先縮短交易。
- 先補索引。
- 先分清楚是不是同一批熱門資料造成競爭。
- 最後才考慮調 timeout。
如果只是一直拉大等待時間,通常只是把尖峰時間的壞味道往後延。
參考資料
MySql Lock wait timeout exceeded该如何处理? | 凝雨 - Yun | 快乐编程每一天 - Happy Coding Every Days
[心得] 高併發伺服器設定 « Huli’s Blog
谈谈MySQL的锁 - 蹲厕所的熊
在laravel上處理高併發問題 - ScottChayaa
MySQL insert into … select 的锁情况 【转】 - 大树叶 技术专栏 - CSDN博客
MySQL InnoDB 锁等待和锁等待超时的处理 - 简书
資料庫交易的 Isolation - getamis - Medium
對於 MySQL Repeatable Read Isolation 常見的三個誤解 - Chester Chu - Medium
[SQL]建立Index來避免DeadLock | 亂馬客 - 點部落
insert into a select * from b 锁问题分析 | 没有伞的孩子必须努力奔跑