為什么刪除記錄表文件不會減小?(記錄的插入與刪除在磁盤上的變化)


如果你熟悉 MySQL 緩沖池(不熟悉可以查看 一條 sql 的執行過程詳解),可能會覺得是因為刪除操作只更新到緩沖池和 redo log,並沒有進行 flush 落盤,但如果關閉數據庫,觸發 flush ,會發現表文件大小還是不會改變,這是為什么?

原因

首先要了解數據的存儲方式,存儲方式共有兩種,是由參數 innodb_file_per_table 來控制的。

off:共享表結構,表示所有的文件數據存儲在同一個文件中,這樣在刪除整張表后空間也不會被回收,只是被位置被標記為可重用,下次創建表可能就在該位置創建。

on:表示每張表的數據各用一個文件來存儲,在刪除整張表后該文件也會被回收,減小總占用空間。這也是默認的使用方式。如果存儲引擎是 InnoDB ,那么數據文件就是.ibd 格式的,如果是 MyISAM,那么文件就是 .MYD 格式的。

 

雖然執行 drop 刪除表時會減小表文件大小,但在刪除記錄時還是不能減小結構,這個原因與上面的 off 共享表結構很像,因為 數據頁是 InnoDB 管理數據的最小的磁盤單位,數據頁就相當於上面的 "一張表的數據",因為一張表的數據頁都是存在同一個文件中的,所以在執行 delete 刪除數據后只會將將改位置標記可重用,並不會回收,而如果刪除整個頁,那么也只能將該頁標記為可重用而不會回收。這種刪除了但是沒有被回收的位置就稱為 "數據空洞"。

頁合並與頁分裂

頁合並:既然產生了數據空洞,那么數據文件將會變得越來越大,這樣是很不利的,所以 MySQL 會在數據空洞達到一定比例后出觸發 "頁合並",觸發的頁會找最靠近的可以合並的頁進行合並來優化空間(只會將數據頁使用權騰出來,並不會減小表文件大小),防止后續的數據插入使用更多的數據頁造成文件更大。

頁分裂:頁分裂是在插入操作時操作的記錄主鍵 ID 在原本的記錄之間時產生的,因為記錄存儲在數據頁中,如果該數據頁沒有合適的位置來存儲這條記錄,那么就會將該條記錄以及后面的記錄另開要一個數據頁來存儲。

優化:因為頁合並和頁分裂都需要消耗額外的性能。所以我們在插入數據時應當按主鍵遞增順序插入(主鍵可以使用自增ID 或 雪花算法,但如果業務字段有唯一字段且沒有其他索引,那么可以使用其作為主鍵來避免每次查詢都需要回表),刪除數據時按主鍵順序刪除。

 

如何減小表文件

1、自動觸發的頁合並。

2、手動觸發清理大部分的數據空洞(5.6 的 Online DDL 可能會存一些寫操作,可能會產生一些數據空洞),具體做法就是執行 "Alter table 表名 engine = InnoDB",因為 Alter 語句是修改表結構,而執行一個空修改操作就可以在不修改結構的情況下將數據空洞清除。具體原理是會先創建一個臨時表,將當前表中的所有記錄依次添加到臨時表中,最后再將臨時表替換原表的表。但是重建表並一定就是最緊湊的,因為在重建時每個數據頁會留 1/16 用於更新,同時 5.6 后可能還會在向臨時表遷移數據時積累一些寫操作造成頁分裂。而在這過程中不能有其他操作干擾,比如修改數據、讀數據,所以在執行此操作時會添加 MDL 寫鎖,而在執行讀寫操作時會添加 MDL 讀鎖,兩者互斥。

關於 MDL 鎖的解析可查看博客 Mysql 中的MDL 。

 

參考博客:

InnoDB中的頁合並與分裂


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM