MySQL實戰 | 13 爲什麼表數據刪掉一半,表文件大小不變? 數據刪除流程 增刪改-造成空洞 重建表-清除空洞

經常會有同學來問我,我的數據庫佔用空間太大,我把一個最大的表刪掉了一半的數據,怎麼表文件的大小還是沒變?

InnoDB 表的組成:表結構定義和數據。

數據刪除流程

InnoDB 中,數據是以 B+ 樹結構來存儲數據的:

假設刪掉表記錄 R4,此時 InnoDB 只是把 R4 標記爲已刪除狀態,後續這個位置可以插入新的數據,但是磁盤文件大小並不會變化。

何時複用?

比如,插入一個 ID 是 400 的記錄,就可以直接複用原來 R4 的空間,若插入的是 800,爲了保持 B+ 樹的結構,就不能複用該空間了。

刪除整頁數據

當刪除了整頁數據後,InnoDB 會將該頁標記爲已刪除,整頁都可複用。

同時,若兩個相鄰的數據頁利用率都比較低,系統會把兩頁上的數據整合到一個頁中,另一個頁就會標記爲可複用。

刪除整個表

此時,所有的數據頁都會標記爲可複用,但是磁盤空間仍然不會變小。

總結

delete 操作,只是把記錄的位置標記爲「可複用」,但是磁盤大小不會變化,這些可以複用,而未被使用的空間,看起來就像空洞

增刪改-造成空洞

當數據是隨機插入時,就可能造成索引的數據頁分裂。

如圖所示,由於 page A 寫滿,此時插入 ID 爲 550 的數據,就不得不申請新的數據頁,頁分裂完成後,A 上就會留下空洞。

另外,更新索引上的值,其實是刪除舊值,插入新值,這個過程同樣會造成空洞。

綜上,經過大量增刪改操作的表,都是可能存在大量空洞的,若要收縮表空間,就要清除這些空洞。

重建表-清除空洞

如何清除空洞?

直接的想法就是,新建一個表結構相同的表,然後按主鍵 ID 遞增的順序,將原表中的數據,插入到新表。

這樣,新表中就不會存在空洞了。新表的主鍵索引也會更加緊湊,數據頁的利用率也更高。

上面的操作可以通過下面的語句自動完成:

alter table A engine=InnoDB;

MySQL 會自動完成轉存數據、交換表名、刪除舊錶的操作。

詳細流程:

1、新建臨時文件,掃描原表 A 的所有數據頁;
2、根據表 A 的記錄生成 B+ 樹,存儲到臨時文件中;
3、生成臨時文件過程中,對 A 的所有操作都會記錄在一個日誌文件中,對應圖中的 state2 狀態;
4、臨時文件生成後,將日誌文件中的操作應用於臨時文件,得到一個完整的數據文件,對於 state3;
5、用臨時文件替換表 A 的數據文件;

表重建的過程是允許對錶 A 做增刪改操作的,因此是一個 Online DDL(MySQL5.6+)

另外,根據表 A 重建出來的數據是放在“tmp_file”裏的,這個臨時文件是 InnoDB 在內部創建出來的。整個 DDL 過程都在 InnoDB 內部完成。對於 server 層來說,沒有把數據挪動到臨時表,是一個“原地”操作,這就是“inplace”名稱的來源。

上述的這些重建方法都會掃描原表數據和構建臨時文件。對於很大的表來說,這個操作是很消耗 IO 和 CPU 資源的。

因此,如果是線上服務,你要很小心地控制操作時間。如果想要比較安全的操作的話,我推薦你使用 GitHub 開源的 gh-ost 來做。


關注本公衆號,後臺回覆「2018」即可獲取傳智播客 2018 最新 Python 和 Java 教程。

公衆號提供CSDN資源免費下載服務!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章