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() 的時候,需要把資料一行一行地從引擎裡面讀出來,然後累積計數。