1. 程式人生 > 實用技巧 >9.資料表的刪除為什麼大小不變呢?

9.資料表的刪除為什麼大小不變呢?

9.資料表的刪除為什麼大小不變呢?

一個 InnoDB 表包含兩部分,即:表結構定義資料。在 MySQL 8.0 版本以前,表結構是存在以.frm 為字尾的檔案裡。而 MySQL 8.0 版本,則已經允許把表結構定義放在系統資料表中了。因為表結構定義佔用的空間很小,所以我們今天主要討論的是表資料。

引數 innodb_file_per_table表資料既可以存在共享表空間裡,也可以是單獨的檔案。這個行為是由引數 innodb_file_per_table 控制的:

  • 這個引數設定為 OFF 表示的是,表的資料放在系統共享表空間,也就是跟資料字典放在一起;

  • 這個引數設定為 ON 表示的是,每個 InnoDB 表資料儲存在一個以 .ibd 為字尾的檔案中。

    我建議你不論使用 MySQL 的哪個版本,都將這個值設定為 ON。因為,一個表單獨儲存為一個檔案更容易管理,而且在你不需要這個表的時候,通過 drop table 命令,系統就會直接刪除這個檔案。而如果是放在共享表空間中,即使表刪掉了,空間也是不會回收的。

也就是說如果innodb_file_per_table這個引數是 ON 代表是資料儲存在-.IDB的檔案中,那麼drop刪除表就會釋放空間。

如果是OFF的話,在資料表空間儲存的表資料,即使DROP也不會釋放空間,

但是最多的情況是刪除某些行,用delete

我們要刪掉 R4 這個記錄,InnoDB 引擎只會把 R4 這個記錄標記為刪除。如果之後要再插入一個 ID 在 300 和 600 之間的記錄時,可能會複用這個位置。但是,磁碟檔案的大小並不會縮小。

現在,你已經知道了 InnoDB 的資料是按頁儲存的,那麼如果我們刪掉了一個數據頁上的所有記錄,會怎麼樣?答案是,整個資料頁就可以被複用了。並且不用看範圍

就算是,如果我們用 delete 命令把整個表的資料刪除呢?結果就是,所有的資料頁都會被標記為可複用。但是磁碟上,檔案不會變小。

也就是說,通過 delete 命令是不能回收表空間的。這些可以複用,而沒有被使用的空間,看起來就像是“空洞”

不止是刪除資料會造成空洞,插入資料也會。如下圖所示

在資料頁中間插入資料的時候,因為資料頁此時已滿,所以會造成資料頁的分裂,然後就會造成空洞。

更新索引上的值,可以理解為刪除一箇舊的值,再插入一個新值。不難理解,這也是會造成空洞的。

也就是說,經過大量增刪改的表,都是可能是存在空洞的。所以,如果能夠把這些空洞去掉,就能達到收縮表空間的目的。

所以說一張表如果重複的增刪改就會造成空洞,並且頁不會縮小表資料的大小。

那麼重建表的話就會使空洞的表空間得以利用

試想一下,如果你現在有一個表 A,需要做空間收縮,為了把表中存在的空洞去掉,你可以怎麼做呢?你可以新建一個與表 A 結構相同的表 B,然後按照主鍵 ID 遞增的順序,把資料一行一行地從表 A 裡讀出來再插入到表 B 中。由於表 B 是新建的表,所以表 A 主鍵索引上的空洞,在表 B 中就都不存在了。顯然地,表 B 的主鍵索引更緊湊,資料頁的利用率也更高。如果我們把表 B 作為臨時表,資料從表 A 匯入表 B 的操作完成後,用表 B 替換 A,從效果上看,就起到了收縮表 A 空間的作用。

alter table A engine=InnoD

在 MySQL 5.5 版本之前,這個命令的執行流程跟我們前面描述的差不多,區別只是這個臨時表 B 不需要你自己建立,MySQL 會自動完成轉存資料、交換表名、刪除舊錶的操作。花時間最多的步驟是往臨時表插入資料的過程,如果在這個過程中,有新的資料要寫入到表 A 的話,就會造成資料丟失。因此,在整個 DDL 過程中,表 A 中不能有更新。也就是說,這個 DDL 不是 Online 的。

MySQL 5.6 版本開始引入的 Online DDL,對這個操作流程做了優化。

1.建立一個臨時檔案,掃描表 A 主鍵的所有資料頁;

2.用資料頁中表 A 的記錄生成 B+ 樹,儲存到臨時檔案中;

3.生成臨時檔案的過程中,將所有對 A 的操作記錄在一個日誌檔案(row log)中,對應的是圖中 state2 的狀態;

4.臨時檔案生成後,將日誌檔案中的操作應用到臨時檔案,得到一個邏輯資料上與表 A 相同的資料檔案,對應的就是圖中 state3 的狀態;

5.用臨時檔案替換表 A 的資料檔案。

需要補充說明的是,上述的這些重建方法都會掃描原表資料和構建臨時檔案。對於很大的表來說,這個操作是很消耗 IO 和 CPU 資源的。因此,如果是線上服務,你要很小心地控制操作時間。如果想要比較安全的操作的話,我推薦你使用 GitHub 開源的 gh-ost 來做。

  • 從 MySQL 5.6 版本開始,alter table t engine = InnoDB(也就是 recreate)預設的就是上面圖 4 的流程了;

  • analyze table t 其實不是重建表,只是對錶的索引資訊做重新統計,沒有修改資料,這個過程中加了 MDL 讀鎖;

  • optimize table t 等於 recreate+analyze。小結

1:為啥刪除了表的一半數8據,表文檔案大小沒變化?

因為delete 命令其實只是把記錄的位置,或者資料頁標記為了“可複用”,但磁碟檔案的大小是不會變的。也可以認為是一種邏輯刪除,所以物理空間沒有實際釋放,只是標記為可複用,表文件的大小當然是不變的啦!

2:表的資料資訊存在哪裡?

表資料資訊可能較小也可能巨大無比,她可以儲存在共享表空間裡,也可以單獨儲存在一個以.ibd為字尾的檔案裡,由引數innodb_file_per_table來控制,老師建議總是作為一個單獨的檔案來儲存,這樣非常容易管理,並且在不需要的時候,使用drop table命令也能直接把對應的檔案刪除,如果儲存在共享空間之中即使表刪除了空間也不會釋放。

3:表的結構資訊存在哪裡?

首先,表結構定義佔有的儲存空間比較小,在MySQL8.0之前,表結構的定義資訊存在以.frm為字尾的檔案裡,在MySQL8.0之後,則允許把表結構的定義資訊存在系統資料表之中。

系統資料表,主要用於儲存MySQL的系統資料,比如:資料字典、undo log(預設)等檔案

4:如何才能刪除表資料後,表文件大小就變小?

重建表,消除表因為進行大量的增刪改操作而產生的空洞,使用如下命令:

1:alter table t engine=InnoDB

2:optimize table t( 等於 recreate+analyze)。

3:truntace table t (等於drop+create)

5:空洞是啥?咋產生的?

空洞就是那些被標記可複用但是還沒被使用的儲存空間。

使用delete命令刪除資料會產生空洞,標記為可複用

插入新的資料可能引起頁分裂,也可能產生空洞

修改操作,有時是一種先刪後插的動作也可能產生空洞

count() 的實現方式你首先要明確的是,在不同的 MySQL 引擎中,count() 有不同的實現方式。MyISAM 引擎把一個表的總行數存在了磁碟上,因此執行 count() 的時候會直接返回這個數,效率很高;而 InnoDB 引擎就麻煩了,它執行 count() 的時候,需要把資料一行一行地從引擎裡面讀出來,然後累積計數。