MySQL 中刪除的數(shù)據(jù)都去哪兒了?
本文轉載自微信公眾號「SH的全棧筆記」,作者SH的全棧筆記 。轉載本文請聯(lián)系SH的全棧筆記公眾號。
不知道大家有沒有想過下面這件事?
我們平時調用 DELETE 在 MySQL 中刪除的數(shù)據(jù)都去哪兒了?
這還用問嗎?當然是被刪除了啊
那么這里又有個新的問題了,如果在 InnoDB 下,多事務并發(fā)的情況下,如果事務A刪除了 id=1 的數(shù)據(jù),同時事務B又去讀取 id=1 的數(shù)據(jù),如果這條數(shù)據(jù)真的被刪除了,那 MVCC 拿啥數(shù)據(jù)返回給用戶呢?
沒錯,這就需要了解一下 MySQL 的多版本并發(fā)的原理相關的東西,感興趣的可以去看我之前寫的這篇文章。
所以,實際情況中,調用了 DELETE 語句刪除的數(shù)據(jù)并不會真正的被物理刪除,這條數(shù)據(jù)其實還在那,只不過被打上了一個標記,標記已刪除。
這其實跟我們日常的操作——軟刪除,差不多是一個意思
在 MySQL 中, UPDATE 和 DELETE 操作本質上是一樣的, 都屬于更新操作,刪除操作只不過是把某行數(shù)據(jù)中的一個特定的比特位標記為已刪除,僅此而已。
那么問題又來了,那這些刪除的數(shù)據(jù)如果一直這么堆下去,那不早晚把硬盤撐爆?
如果都玩兒成這樣了,那 MySQL 還能像現(xiàn)在這樣被大規(guī)模的用于生產(chǎn)環(huán)境中嗎?那 MySQL 到底是怎么玩的?
這就需要提到 Purge 操作了。
Purge操作是啥?
Purge 操作才是真正將數(shù)據(jù)(已被標記為已刪除)物理刪除的操作。
Purge 操作針對的數(shù)據(jù)對象,不僅僅是某一行,還有其對應的索引數(shù)據(jù)和 Undo Log。
好的那么問題又來了。
問題是,Purge 操作什么時候會執(zhí)行呢?實際上,你可以將執(zhí)行 Purge 操作的線程(簡稱 Purge 線程)理解成一個后臺周期性執(zhí)行的線程。
Purge 線程可以有一個,也可以有多個,具體的線程數(shù)量可以由 MySQL 的配置項 innodb_purge_threads 來進行配置。當然,我相信你肯定不記得在使用 MySQL 的時候配置過這個,因為 innodb_purge_threads 有個默認值,值為 4。
InnoDB 會根據(jù) MySQL 中表的數(shù)量和 Purge 線程的數(shù)量進行分配。
但正是因為有這種特性,Purge 線程的數(shù)量才需要根據(jù)業(yè)務的實際情況來做調整。舉個例子,假設 DML 操作都集中在某張表,比如表1上...
你先等等,我打斷一下......
什么叫 DML 操作?總喜歡搞些復雜的名詞...DML(Data Manipulation Language)數(shù)據(jù)操作語句,實際上就是CRUD增刪改查...
與之類似的概念還有DDL(Data Definition Language)數(shù)據(jù)定義語句,也就是CREATE、DROP和ALTER等等.
以及DCL(Data Control Language)數(shù)據(jù)控制語句,也就是GRANT、REVOKE等等...
繼續(xù)說回來,雖然 Purge 線程的數(shù)量是可配置的,但是也不是你想配多少就配多少的。不然你給它干個 10000 個線程,那不就直接原地 OOM 了嗎?
innodb_purge_threads 的最大值為 32,而且并不是我們配了 32 InnoDB 就真的會啟動 32 個 Purge 線程,為啥呢?舉個很簡單的例子,假設此時只有一張表,然后我們配置了 32 個 Purge 線程。
你看著上面這個圖問問自己,這「河里」嗎?這樣不僅浪費了系統(tǒng)的資源,同時還使得不同的 Purge 線程之間發(fā)生了數(shù)據(jù)競爭。不僅如此,Purge 線程還可能跟用戶線程產(chǎn)生競爭。
但是當系統(tǒng)中真的有 32 張表的時候,情況又不一樣了,一個 Purge 線程對應一張表,線程與線程之間就不會存在數(shù)據(jù)競爭,并且沒有浪費系統(tǒng)資源,還能夠提升執(zhí)行 Purge 操作的性能。
這就是為啥 InnoDB 會根據(jù)實際情況來調整 MySQL 中 Purge 線程的數(shù)量,所以我們在配置的時候也要按照實際情況來設置。
舉個例子,如果你的數(shù)據(jù)庫中,增刪改 的操作只集中在某幾張表上,則可以考慮將 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 的數(shù)據(jù)來源是一個一個 Undo Log 形成的單鏈表,而 Purge 線程就是用于定期清理 Undo Log 的,并且在清理完 刪除數(shù)據(jù)所生成的 Undo Log 的時候,就會把對應的行記錄給移除了。
那么問題又來了,Purge 線程每次會讀取多少條件 Undo Log 記錄呢?
很明顯,它不是看當時的心情來決定取多少條的。它是通過配置項 innodb_purge_batch_size 來控制的,默認是 300。然后InnoDB會將這300條 Undo Log 分給innodb_purge_threads個 Purge 線程。在清理的過程中,Purge 線程還會釋放 Undo Log 表空間內的文件。