Mysql

InnoDB 在送出之前將事務數據儲存在哪裡?

  • October 16, 2015

我在家裡使用 JDBC 技術READ_COMMITTED進行了一些測試。READ_UNCOMMITTED

我看到它READ_UNCOMMITTED實際上可以讀取未送出的數據,例如來自尚未送出的某些事務的數據(可以執行更新查詢)。

問題

  • 未送出的數據儲存在哪裡,以便一個READ_UNCOMMITTED事務可以從另一個事務中讀取未送出的數據?
  • 為什麼事務不可能READ_COMMITTED讀取未送出的數據,即執行“臟讀”?什麼機制強制執行此限制?

未送出的數據儲存在哪裡,這樣一個 READ_UNCOMMITTED 事務可以從另一個事務中讀取未送出的數據? "

新的未送出記錄(集群 PK)版本被視為頁面上記錄的“目前”版本。因此它們可以儲存在緩衝池和/或表空間中(例如 tablename.ibd)。然後需要在 READ-UNCOMMITTED 以外的任何地方建構快照/視圖的事務,需要使用 UNDO 記錄(儲存在系統表空間中)建構行的先前版本(遵循歷史列表)。在讀取未送出的記錄時,InnoDB 可能還需要從Change Buffer中讀取一些未送出的二級索引記錄並應用它們,然後再將記錄呈現給使用者。

正是這種行為可以使 InnoDB 中的回滾相對昂貴。這是一個很大的因素,也可能導致長期執行的持有更新記錄的空閒事務潛在的性能問題,因為這些事務將阻止清除操作並且舊記錄版本的歷史列表增長,以及重建這些舊版本所需的 UNDO 記錄按需,將繼續增長。它減慢了需要讀取舊版本/已送出記錄的新事務,因為它們需要遍歷越來越長的歷史列表——這是一個 UNDO 記錄的單鍊錶——並做更多的工作來重建舊版唱片。所以你最終會使用大量的 CPU 週期(更不用說內部鎖定原語:互斥鎖、rw_locks、信號量等)。

希望這是有道理的?:)

僅供參考,在 MySQL 5.7 中,您可以將 UNDO 表空間和日誌移出系統表空間,並自動截斷它們。如果您有一個阻止清除操作的長時間執行的事務,它們可能會變得非常大,從而導致非常長且不斷增長的歷史列表長度。將它們儲存在系統表空間中是導致 ibdata1 文件巨大/增長的最常見原因,而該文件又不能被截斷/縮小/清理以稍後回收該空間。

你問

未送出的數據儲存在哪裡,以便 READ_UNCOMMITTED 事務可以從另一個事務中讀取未送出的數據?

為了回答您的問題,您需要了解 InnoDB 架構是什麼樣的。

下圖是多年前 Percona 首席技術官 Vadim Tkachenko 創建的

InnoDB 架構

根據關於InnoDB 事務模型和鎖定的MySQL 文件

COMMIT 意味著在目前事務中所做的更改是永久的,並且對其他會話可見。另一方面,ROLLBACK 語句取消目前事務所做的所有修改。COMMIT 和 ROLLBACK 都會釋放在目前事務期間設置的所有 InnoDB 鎖。

由於 COMMIT 和 ROLLBACK 控制數據可見性,READ COMMITTED 和 READ UNCOMMITTED 將不得不依賴於記錄更改的結構和機制

  1. 回滾段/撤消空間
  2. 重做日誌
  3. 所涉及的桌子上的間隙鎖

回滾段和撤消空間會在應用更改之前知道更改的數據是什麼樣的。重做日誌將知道要前滾哪些更改以使數據出現更新。

你還問

為什麼 READ_COMMITTED 事務不能讀取未送出的數據,即執行“臟讀”?什麼機制強制執行此限制?

重做日誌、撤消空間和鎖定行開始發揮作用。您還必須考慮 InnoDB 緩衝池(您可以在其中使用innodb_max_dirty_pages_pctinnodb_buffer_pool_pages_dirtyinnodb_buffer_pool_bytes_dirty測量臟頁)。

鑑於此,READ COMMITTED 將知道數據永久顯示的樣子。因此,無需查找未送出的髒頁。READ COMMITED 只不過是已送出的髒讀。READ UNCOMMITTED 將繼續知道要鎖定哪些行以及已讀取或忽略哪些重做日誌以使數據可見。

要完全理解鎖定行來管理隔離,請閱讀InnoDB 事務模型和鎖定

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