MySQL 中刪除的資料都去哪兒了?

detectiveHLH發表於2021-08-11

不知道大家有沒有想過下面這件事?

我們平時呼叫 DELETE 在 MySQL 中刪除的資料都去哪兒了?

這還用問嗎?當然是被刪除了啊

那麼這裡又有個新的問題了,如果在 InnoDB 下,多事務併發的情況下,如果事務A刪除了 id=1 的資料,同時事務B又去讀取 id=1 的資料,如果這條資料真的被刪除了,那 MVCC 拿啥資料返回給使用者呢?

沒錯,這就需要了解一下 MySQL 的多版本併發的原理相關的東西,感興趣的可以去看我之前寫的這篇文章

所以,實際情況中,呼叫了 DELETE 語句刪除的資料並不會真正的被物理刪除,這條資料其實還在那,只不過被打上了一個標記,標記已刪除

這其實跟我們日常的操作——軟刪除,差不多是一個意思

在 MySQL 中, UPDATEDELETE 操作本質上是一樣的, 都屬於更新操作,刪除操作只不過是把某行資料中的一個特定的位元位標記為已刪除,僅此而已。

那麼問題又來了,那這些刪除的資料如果一直這麼堆下去,那不早晚把硬碟撐爆?

如果都玩兒成這樣了,那 MySQL 還能像現在這樣被大規模的用於生產環境中嗎?那 MySQL 到底是怎麼玩的?

這就需要提到 Purge 操作了。

Purge操作是啥?

Purge 操作才是真正將資料(已被標記為已刪除)物理刪除的操作。

Purge 操作針對的資料物件,不僅僅是某一行,還有其對應的索引資料和 Undo Log。

好的那麼問題又來了。

問題是,Purge 操作什麼時候會執行呢?實際上,你可以將執行 Purge 操作的執行緒(簡稱 Purge 執行緒)理解成一個後臺週期性執行的執行緒。

Purge 執行緒可以有一個,也可以有多個,具體的執行緒數量可以由 MySQL 的配置項 innodb_purge_threads 來進行配置。當然,我相信你肯定不記得在使用 MySQL 的時候配置過這個,因為 innodb_purge_threads 有個預設值,值為 4

InnoDB 會根據 MySQL 中表的數量和 Purge 執行緒的數量進行分配。

但正是因為有這種特性,Purge 執行緒的數量才需要根據業務的實際情況來做調整。舉個例子,假設 DML 操作都集中在某張表,比如表1上...

你先等等,我打斷一下......

什麼叫 DML 操作?總喜歡搞些複雜的名詞...DML(Data Manipulation Language)資料操作語句,實際上就是CRUD增刪改查...

與之類似的概念還有DDL(Data Definition Language)資料定義語句,也就是CREATEDROPALTER等等.

以及DCL(Data Control Language)資料控制語句,也就是GRANTREVOKE等等...

繼續說回來,雖然 Purge 執行緒的數量是可配置的,但是也不是你想配多少就配多少的。不然你給它幹個 10000 個執行緒,那不就直接原地 OOM 了嗎?

innodb_purge_threads 的最大值為 32,而且並不是我們配了 32 InnoDB 就真的會啟動 32 個 Purge 執行緒,為啥呢?舉個很簡單的例子,假設此時只有一張表,然後我們配置了 32 個 Purge 執行緒。

你看著上面這個圖問問自己,這「河裡」嗎?這樣不僅浪費了系統的資源,同時還使得不同的 Purge 執行緒之間發生了資料競爭。不僅如此,Purge 執行緒還可能跟使用者執行緒產生競爭。

但是當系統中真的有 32 張表的時候,情況又不一樣了,一個 Purge 執行緒對應一張表,執行緒與執行緒之間就不會存在資料競爭,並且沒有浪費系統資源,還能夠提升執行 Purge 操作的效能。

這就是為啥 InnoDB 會根據實際情況來調整 MySQL 中 Purge 執行緒的數量,所以我們在配置的時候也要按照實際情況來設定。

舉個例子,如果你的資料庫中,增刪改 的操作只集中在某幾張表上,則可以考慮將 innodb_purge_threads 設定的稍微低一點。相反,如果 增刪改 的操作幾乎每張表都有,那麼 innodb_purge_threads 就可以設定的大一些。

瞭解完 Purge 執行緒本身之後,我們就可以來了解 Purge 執行緒所針對的物件了。Purge 執行緒主要清理的物件是 Undo Logs,其次是行記錄。

因為 Undo Log 可以分為:

  • Insert Undo Log
  • Update Undo Log

所以更準確的說法是,Purge 執行緒清理的物件是 Update Undo Log 和 行記錄,因為 Insert Undo Log 會在事務提交之後就會被刪除。

我們都知道 InnoDB 的 MVCC 的資料來源是一個一個 Undo Log 形成的單連結串列,而 Purge 執行緒就是用於定期清理 Undo Log 的,並且在清理完 刪除資料所生成的 Undo Log 的時候,就會把對應的行記錄給移除了。

那麼問題又來了,Purge 執行緒每次會讀取多少條件 Undo Log 記錄呢?

很明顯,它不是看當時的心情來決定取多少條的。它是通過配置項 innodb_purge_batch_size 來控制的,預設是 300。然後InnoDB會將這300條 Undo Log 分給innodb_purge_threads個 Purge 執行緒。在清理的過程中,Purge 執行緒還會釋放 Undo Log 表空間內的檔案。

本篇文章已放到我的 Github github.com/sh-blog 中,歡迎 Star。微信搜尋關注【SH的全棧筆記】,回覆【佇列】獲取MQ學習資料,包含基礎概念解析和RocketMQ詳細的原始碼解析,持續更新中。

如果你覺得這篇文章對你有幫助,還麻煩點個贊關個注分個享留個言

相關文章