Laravel SQL Server Queue
Laravel Queue 在 SQL Server 上遇到的 deadlock,怎麼排查
問題
專案的佇列表(jobs table)建在 SQL Server 上,某天開始陸續收到
SQLSTATE[40001]: Serialization failure: 1205 Transaction (Process ID xx) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.
worker 數量調到 4 個之後才開始出現,單一 worker 完全沒事。
排查
先用 SQL Server 內建的 deadlock graph 確認卡住的兩個查詢,發現都是 Laravel Queue 的
reserveJobsByPollTime 在搶同一批 jobs 資料列。SQL Server 預設的 READ COMMITTED
搭配 UPDLOCK 在高併發搶鎖時,很容易兩個 transaction 互相等對方釋放鎖。
MySQL 上用 SELECT ... FOR UPDATE SKIP LOCKED 完全沒問題,但 SQL Server 的
Laravel 佇列 driver 對 SKIP LOCKED 語法支援不完整,這是這次問題的根本原因。
解法
兩個方向都用上了:
- 把
queue.php裡對應連線的retry_after拉高,減少同一個 Job 被重複搶的機率。 - 資料庫連線層加上
READPASThint,讓 worker 搶鎖時直接跳過已被鎖住的資料列, 而不是等待,行為上更接近 MySQL 的SKIP LOCKED。
改完之後同樣的併發量下,deadlock 訊息就沒再出現過。如果專案一開始就能選 MySQL 或 Redis 當佇列後端,其實可以少踩這個雷,但既有系統要接公部門的 SQL Server,這條路還是得走一次。