多個連接上的事務行為
我有一個帶有 InnoDB 表的 MariaDB 數據庫。我有兩個到同一個數據庫的連接。一個連接正在使用圍繞大量工作的事務。另一個連接的任務是更新同一數據庫中的表(但在事務工作範圍之外)以跟踪事務的進度。
所有這些都發生在同一個腳本中,但同樣是使用單獨的連接:一個用於正在執行的實際工作(包括事務),另一個用於保存有關工作進度的元資訊。
我預計這會起作用,這樣作業跟踪記錄會隨著作業的進行而更新 - 10%、20%、50% 等,因為事務工作是在單獨的連接上完成並影響其他表。但是,在我的測試中,在送出作業的事務之前,作業跟踪記錄不會更新。
據我所知,它沒有死鎖,因為“進度監控”連接根本沒有使用事務。“worker”連接將其查詢包裝在事務中,但另一個連接沒有。
我通過查看我在工作進行時填寫的“完成百分比”欄位來檢查進度。操作順序為:
- 連接一:開始交易
- 連接 1:添加和更新行
- 連接2:更新進度
- 連接 1:添加和更新更多行。
然後,在另一個 HTTP 連接中,我正在輪詢並輸出進度值,但在連接 1 上的事務送出之前它永遠不會更新。
除了將工作跟踪表移動到完全不同的數據庫之外,還有什麼方法可以獲得我想要的行為?
其他建議的解決方案似乎都不適合我。相反,我最終將“工作進度”表從 InnoDB 切換到 MyISAM。由於 MyISAM 不支持事務,因此即使在事務期間,記錄也會立即寫入表中。
將來,我希望有一種替代方法將所有表保留在 InnoDB 中以保持一致性,但這是我發現的唯一對我有用的解決方案。
您看到的是預設隔離設置 (
REPEATABLE READ
) 甚至較低 (READ COMMITTED
) 設置的正確行為。更多細節可以在 MariaDB 和 MySQL 文件中找到:基本上總結為:
一個事務正在寫入什麼,其他事務應該無法讀取。不是在一個事務送出之前。
當事務正在向數據庫寫入內容時,無法保證寫入會持續存在。事務可能會出錯,並且它所做的所有寫入都會回滾。所以,你觀察到的
但是,在我的測試中,在送出作業的事務之前,作業跟踪記錄不會更新。
從這個角度來看是正確的。從外部看不到任何東西,進度為 0,直到有東西被送出。這就是事務的隔離屬性的全部意義所在。
我看到這個問題的 2 個解決方案:
- A. 也使用“worker”,事務 1 進行進度報告。有誰比工人本身更清楚它的工作進展了多少?
- B. 在第 2 個“進度報告”連接中,使用最低隔離設置 (
READ UNCOMMITTED
):SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
現在,來自該連接的事務將能夠進行“臟讀”,即從第一個事務中查看未送出的寫入,並將正確報告(未送出的)進度。