Mysql
InnoDB 事務重試的意外行為
這將很難重現,但希望有人可以根據所涉及的邏輯對我的問題有所了解。在事務期間遇到一些間歇性死鎖問題後,我實施了一個重試策略,類似於這樣(PHP):
public function execute_query($sql) { $try = 1; $log_msg = ''; while (true) { $query = $this->link->query($sql); $error_no = $this->link->errno; $error_msg = $this->link->error; if (!$error_no){ if ($try > 1) { $this->debug_log->write('Deadlock Succeeded after ' . $try . ' Tries ::: ' . $sql); } return $query; } else { $log_msg = 'Error: ' . $error_msg . ' ::: Error No: ' . $error_no . ' ::: ' . ($try > 1 ? $try . ' Tries ::: ' : '') . $sql; $this->debug_log->write($log_msg); if ($error_no == 1213 && $try < self::DEADLOCK_RETRY) { // retry when deadlock occurs sleep($try * 2); $try++; } else { throw new ErrorException($log_msg); exit(); } } } }
這似乎產生了與我所希望的完全相反的影響——我的事務的前半部分似乎被回滾,但後半部分送出。
例如:
START TRANSACTION; Query 1 Query 2 Query 3 (deadlock occurs, query gets retried and succeeds on 2nd attempt) Query 4 Query 5 COMMIT;
在所有這一切結束時,我留下了查詢 3、4 和 5 成功執行,但查詢 1 和 2 沒有。我不明白這是怎麼可能的,但現在已經發生了兩次,我無法重現死鎖來測試或製定工作策略。
誰能解釋為什麼在 InnoDB 事務中重試失敗的查詢會導致一半事務回滾而另一半送出?
當查詢 3 發生死鎖時,死鎖可能涉及查詢 1 或 2。您必須重新開始
START TRANSACTION
並重播所有查詢。如果其中任何一個是控制
SELECTs
的結果,SELECT
隨後將是什麼UPDATEd
,DELETEd
等等,那麼你有FOR UPDATE
嗎SELECT
?您必須在事務中的每個查詢之後檢查錯誤。如果發生死鎖,請重新開始,如上所述。
發生了什麼
- 錯誤發生了,所以它回滾了事務。
- 客戶端程式碼忽略了錯誤並繼續前進。
- 查詢 4、5、6 已執行並送出。
需要什麼
- 錯誤發生了,所以它回滾了事務。
- 客戶端程式碼注意到了這個錯誤。
- 客戶端程式碼應該有程式碼
ROLLBACK
並跳轉回START
.(顯式
ROLLBACK
可能是多餘的,但無害。但是,分支回到START
客戶端程式碼。)應該以這種方式處理“死鎖”,但任何其他“錯誤”都應通過中止處理。
而且,由於幾乎在任何查詢之後都可能發生死鎖,因此您應該在任何地方進行測試。