Mysql

InnoDB 的撤消日誌在具有 READ-UNCOMMITTED 級別的簡單 SELECT 後開始增長

  • December 22, 2021

我有一個具有 20K QPS(大量插入/更新/刪除)的 percona mysql (5.7) 伺服器。

我的問題是:為什麼使用 trx isolation=READ-UNCOMMITTED 發出一個簡單但很長的選擇查詢(對任何表)會使歷史列表長度(撤消日誌)增長?(查詢執行後,歷史列表立即被清除)。

這是程式碼範例:

SET TRANSACTION ISOLATION LEVEL READ-UNCOMMITTED;
SELECT tt.id, tt.name, SLEEP(1) AS delay FROM table tt; # 100 rows, takes 100 to execute

據我了解,該 SELECT 查詢不需要讀取視圖,也不關心數據一致性。它只是讀取臟的、可能未送出的數據。但是,在執行過程中,歷史列表長度開始快速增長(由於 QPS 高,其他事務寫入很多),導致 mysql 無法清除 - 但是為什麼呢?我的事務處於 READ-UNCOMMITTED 模式,它永遠不需要舊版本的行,對吧?(如果我錯了,請在這裡糾正我)。

我只是無法在這裡抓住mysql的邏輯..需要幫助。

PS我檢查了“information_schema.INNODB_TRX”。它顯示 trx 隔離=READ-UNCOMMITTED,trx_read_only=1,trx_autocommit_non_blocking=1。我檢查了其他長時間執行的事務,沒有。我在 4 個不同的副本和主副本上重複了這個 - 相同的行為。我嘗試在更大的桌子上不使用“SLEEP(1)”——同樣的行為。我在一個複製品上重複了這個,沒有任何連接,只有我的和復製品的踏板(所以沒有人可以影響測試)。

UPD 1(供評論):

我做了一個測試。創建一個表,插入 4 行。

CREATE TABLE my_table (
 id INT(11) NOT NULL AUTO_INCREMENT
, text VARCHAR(255) DEFAULT NULL
, PRIMARY KEY (id)
) ENGINE = INNODB;

INSERT INTO my_table(id, text) VALUES (1, 'hi'), (3, 'hi 2'), (5, 'hi 3'), (7, 'hi 5');

開始臟讀,會話 2 將在該查詢開始後插入行

# session 1, now 2021-12-22 21:20:00
   SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
   SELECT tt.id, tt.text, SLEEP(4) AS delay FROM my_table tt;

在會話 1 開始後開始在此處插入行

# session 2, now 2021-12-22 21:20:22
   INSERT INTO my_table(text) VALUES('hi 7');
   INSERT INTO my_table(text) VALUES('hi 11'); ....

依此類推,我們可以永遠插入行,並且會話 1 中的查詢永遠不會完成

根據mysqlperformanceblog.com 的 Reasons for run-away main Innodb Tablespace,您可能遇到這些事情的原因如下:

執行進行大量更改的事務——如果事務修改了很多行,它必須在撤消段中使用大量空間,並且沒有辦法繞過它。特別要小心更新或刪除跨越很多行的事務。在許多情況下,以塊的形式進行此類處理,如果您的應用程序可以處理它,每個事務可能會更新/刪除數千行可能會更好。注意即使對於非常大的表,ALTER TABLE 也不需要過多的撤消空間,因為它在內部每 10000 行送出一次。

執行很長的事務如果你執行很長的事務,甚至是 SELECT,Innodb 將無法清除在此事務開始後完成的更改記錄,預設為 REPEATABLE-READ 隔離模式。這意味著非常長的事務非常糟糕,會導致數據庫中容納大量垃圾。它不限於撤消槽。當我們談論 Long Transactions 時,時間是一個不好的衡量標準。只讀數據庫中的事務打開數週並沒有什麼壞處,但是如果數據庫具有非常高的更新率,例如每秒修改 10K+ 行,甚至可以考慮 5 分鐘的事務,只要它足以累積大約 300 萬行更改.

清除執行緒落後這是最危險的原因。數據庫更新發生的速度可能比清除執行緒在不再需要記錄時清除記錄的速度更快,這意味著撤消空間可能會增長,直到它消耗所有可用空間(或為 ibdata1 文件指定的最大大小)。“好”的事情是性能通常在此之前就開始受到嚴重影響,並且會引起注意。如果清除執行緒跟不上,您可以首先針對此問題做一些事情,您可以使用innodb_max_purge_lag使進行修改的執行緒變慢。然而,這並不適用於所有情況。如果您正在執行 XtraDB,您還可以使用innodb_use_purge_thread使用專用的清除執行緒,它的工作速度更快,因為它不需要與 main theread 的其他活動競爭。您還可以通過將此變數設置為更高的值來使用多個清除執行緒,儘管此功能有點實驗性。

很多年前我就提到過這個文章…

可能的建議

  • 確保你殺死所有的睡眠連接。他們可能仍然持有隱形桌鎖。在某些情況下,InnoDB 會在回滾後保留表鎖而不知道是誰製作了鎖(請參閱我May 13, 2012的文章無法更新 innodb 表中的某些行)。如果此類鎖存在並隨後被移除,則歷史長度應該會消散。
  • 也許在副本上嘗試使用innodb_max_purge_lag來查看歷史長度是否下降。

使用 tx_isolation=read uncommitted 我的查詢的讀取視圖是否應該阻止 mysql 清除撤消日誌?在我的例子中,當查詢開始時,我看到歷史列表的即時跳躍和查詢完成時的即時下降。

是的,這是正確的行為。清除舊的行版本不能超過給定執行查詢所需的行版本。

無論您的tx_isolation. 意思是,查詢只看到查詢開始時存在的行。如果在查詢執行期間創建了新行,則該查詢仍然看不到新行。就好像每個查詢都有自己的 REPEATABLE-READ 隔離,一直持續到查詢執行結束。

這意味著無法清除該查詢的數據庫視圖所需的行版本,至少在查詢完成之前是這樣。使用 READ-UNCOMMITTED 不會改變這一點。

當您使用 READ-COMMITTED 或 READ-UNCOMMITTED 時,可以在查詢完成清除行版本,但事務尚未完成。

當您使用 REPEATABLE-READ 時,直到事務完成後才能清除行版本。

據我所知,沒有合法使用 READ-UNCOMMITTED 。它不會減少鎖定需求,也不會減少快照需求,它只會導致您的事務可以查看異常數據的情況(例如,在其他事務中部分執行更改)。

我建議永遠不要使用 READ-UNCOMMITTED。

引用自:https://dba.stackexchange.com/questions/305258