文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  ...  /  InnoDB 表格的壓縮運作方式

17.9.1.5 InnoDB 表格的壓縮運作方式

本節說明有關 InnoDB 表格壓縮的一些內部實作詳細資訊。此處提供的資訊可能有助於調整效能,但對於壓縮的基本使用並非必須了解。

壓縮演算法

某些作業系統會在檔案系統層級實作壓縮。檔案通常會分成固定大小的區塊,這些區塊會壓縮成可變大小的區塊,這很容易導致片段化。每次修改區塊內部的內容時,整個區塊會在寫入磁碟之前重新壓縮。這些特性使這種壓縮技術不適合在更新密集的資料庫系統中使用。

MySQL 在著名的 zlib 程式庫的協助下實作壓縮,該程式庫實作 LZ77 壓縮演算法。這種壓縮演算法成熟、穩固,並且在 CPU 使用率和減少資料大小方面都很有效率。該演算法是無損的,因此原始未壓縮的資料始終可以從壓縮形式重建。LZ77 壓縮的運作方式是尋找要壓縮的資料內重複的資料序列。資料中的值模式會決定壓縮效果,但一般使用者資料通常會壓縮 50% 或更多。

與應用程式執行的壓縮或某些其他資料庫管理系統的壓縮功能不同,InnoDB 壓縮同時適用於使用者資料和索引。在許多情況下,索引可能佔總資料庫大小的 40-50% 或更多,因此這種差異非常顯著。當壓縮對於資料集運作良好時,InnoDB 資料檔案(單表檔案表空間或通用表空間 .ibd 檔案)的大小為未壓縮大小的 25% 至 50%,甚至可能更小。根據工作負載的不同,這個較小的資料庫反過來可以減少 I/O 並提高吞吐量,而 CPU 利用率的成本僅略有增加。您可以透過修改 innodb_compression_level 組態選項來調整壓縮等級和 CPU 負擔之間的平衡。

InnoDB 資料儲存和壓縮

InnoDB 資料表中的所有使用者資料都儲存在包含 B 樹索引(叢集索引)的頁面中。在某些其他資料庫系統中,此類型的索引稱為索引組織表。索引節點中的每一列都包含(使用者指定或系統產生的)主鍵的值以及資料表的所有其他欄位。

InnoDB 資料表中的次要索引也是 B 樹,其中包含成對的值:索引鍵和指向叢集索引中某一列的指標。此指標實際上是資料表主鍵的值,如果需要索引鍵和主鍵以外的欄位,則會使用該值來存取叢集索引。次要索引記錄必須始終符合單一 B 樹頁面。

B 樹節點(叢集索引和次要索引)的壓縮與用於儲存長 VARCHARBLOBTEXT 欄位的溢出頁面的壓縮方式不同,如下節所述。

B 樹頁面的壓縮

由於 B 樹頁面經常更新,因此需要特殊處理。盡量減少 B 樹節點分割的次數,以及盡量減少解壓縮和重新壓縮其內容的需求非常重要。

MySQL 使用的一種技術是在 B 樹節點中以未壓縮的形式維護一些系統資訊,從而促進某些就地更新。例如,這樣可以刪除標記和刪除列,而無需任何壓縮操作。

此外,MySQL 會嘗試避免在變更索引頁面時進行不必要的解壓縮和重新壓縮。在每個 B 樹頁面中,系統都會保留一個未壓縮的修改日誌,以記錄對該頁面所做的變更。小的記錄的更新和插入可以寫入此修改日誌,而無需完全重建整個頁面。

當修改日誌的空間耗盡時,InnoDB 會解壓縮該頁面、套用變更並重新壓縮該頁面。如果重新壓縮失敗(稱為壓縮失敗的情況),則會分割 B 樹節點,並重複該過程,直到更新或插入成功為止。

為了避免在寫入密集型工作負載(例如 OLTP 應用程式)中頻繁發生壓縮失敗,MySQL 有時會在頁面中保留一些空白空間(填補),以便修改日誌更快填滿,並且在仍有足夠空間避免分割的情況下重新壓縮頁面。每個頁面中保留的填補空間量會隨著系統追蹤頁面分割的頻率而變化。在繁忙的伺服器上頻繁地寫入壓縮資料表時,您可以調整 innodb_compression_failure_threshold_pctinnodb_compression_pad_pct_max 組態選項來微調此機制。

一般而言,MySQL 要求 InnoDB 資料表中的每個 B 樹頁面至少可以容納兩筆記錄。對於壓縮資料表,此要求已放寬。B 樹節點的葉頁面(無論是主鍵還是次要索引的葉頁面)只需要容納一筆記錄,但該記錄必須以未壓縮的形式符合每個頁面的修改日誌。如果 innodb_strict_modeON,則 MySQL 會在 CREATE TABLECREATE INDEX 期間檢查最大列大小。如果該列不符合,則會發出以下錯誤訊息:ERROR HY000: Too big row

如果您在 innodb_strict_mode 為 OFF 時建立資料表,且後續的 INSERTUPDATE 陳述式嘗試建立不符合壓縮頁面大小的索引項目,則操作會失敗,並顯示 ERROR 42000: Row size too large。(此錯誤訊息不會指定記錄過大的索引名稱,也不會提及索引記錄的長度或該特定索引頁面上的最大記錄大小。)若要解決此問題,請使用 ALTER TABLE 重新建立資料表,並選取較大的壓縮頁面大小 (KEY_BLOCK_SIZE)、縮短任何欄位前置詞索引,或使用 ROW_FORMAT=DYNAMICROW_FORMAT=COMPACT 完全停用壓縮。

innodb_strict_mode 不適用於也支援壓縮資料表的通用表空間。通用表空間的表空間管理規則是獨立於 innodb_strict_mode 嚴格強制執行的。如需更多資訊,請參閱第 15.1.21 節「CREATE TABLESPACE 陳述式」

壓縮 BLOB、VARCHAR 和 TEXT 欄位

在 InnoDB 資料表中,不屬於主鍵一部分的 BLOBVARCHARTEXT 欄位可以儲存在單獨配置的溢出頁面上。我們將這些欄位稱為頁面外欄位。它們的值儲存在溢出頁面的單一連結清單中。

對於在 ROW_FORMAT=DYNAMICROW_FORMAT=COMPRESSED 中建立的資料表,BLOBTEXTVARCHAR 欄位的值可能會完全儲存在頁面外,具體取決於它們的長度和整列的長度。對於儲存在頁面外的欄位,叢集索引記錄僅包含指向溢出頁面的 20 位元組指標(每個欄位一個)。任何欄位是否儲存在頁面外取決於頁面大小和整列的大小。當該列太長而無法完全符合叢集索引的頁面時,MySQL 會選擇最長的欄位進行頁面外儲存,直到該列符合叢集索引頁面為止。如上所述,如果列本身不符合壓縮頁面,則會發生錯誤。

注意

對於在 ROW_FORMAT=DYNAMICROW_FORMAT=COMPRESSED 中建立的資料表,小於或等於 40 個位元組的 TEXTBLOB 欄位始終會內嵌儲存。

使用 ROW_FORMAT=REDUNDANTROW_FORMAT=COMPACT 的資料表會將 BLOBVARCHARTEXT 欄位的前 768 個位元組與主鍵一起儲存在叢集索引記錄中。768 位元組的前置詞後面接著一個指向溢出頁面的 20 位元組指標,其中包含欄位值的其餘部分。

當資料表採用 COMPRESSED 格式時,寫入溢出頁面的所有資料都會依原樣壓縮;也就是說,MySQL 會將 zlib 壓縮演算法套用至整個資料項目。除了資料之外,壓縮的溢出頁面還包含未壓縮的頁首和頁尾,其中包括頁面總和檢查碼以及指向下一個溢出頁面的連結等。因此,如果資料具有高度可壓縮性(文字資料通常如此),則可以為較長的 BLOBTEXTVARCHAR 欄位節省非常顯著的儲存空間。影像資料(例如 JPEG)通常已經壓縮,因此從儲存在壓縮資料表中並不會獲得太多好處;重複壓縮可能會浪費 CPU 週期,而節省的空間卻很少或沒有。

溢出頁面與其他頁面大小相同。即使欄位的總長度僅為 8K 位元組,包含十個儲存在頁面外欄位的列也會佔用十個溢出頁面。在未壓縮的資料表中,十個未壓縮的溢出頁面會佔用 160K 位元組。在頁面大小為 8K 的壓縮資料表中,它們僅佔用 80K 位元組。因此,對於具有長欄位值的資料表,通常更有效率的做法是使用壓縮資料表格式。

對於單表檔案表空間,使用 16K 壓縮頁面大小可以降低 BLOBVARCHARTEXT 欄位的儲存和 I/O 成本,因為這些資料通常壓縮效果良好,因此可能需要較少的溢位頁面,即使 B 樹節點本身佔用的頁面數量與未壓縮的形式相同。一般表空間不支援 16K 壓縮頁面大小 ( KEY_BLOCK_SIZE)。如需更多資訊,請參閱第 17.6.3.3 節「一般表空間」

壓縮和 InnoDB 緩衝池

在壓縮的 InnoDB 表格中,每個壓縮頁面(無論是 1K、2K、4K 或 8K)都對應到 16K 位元組的未壓縮頁面(如果設定了innodb_page_size,則大小會較小)。為了存取頁面中的資料,如果頁面尚未在緩衝池中,MySQL 會從磁碟讀取壓縮頁面,然後將頁面解壓縮回原始形式。本節說明 InnoDB 如何管理壓縮表格頁面的緩衝池。

為了盡量減少 I/O 並減少解壓縮頁面的需求,緩衝池有時會同時包含資料庫頁面的壓縮和未壓縮形式。為了替其他所需的資料庫頁面騰出空間,MySQL 可以從緩衝池逐出未壓縮的頁面,同時將壓縮頁面留在記憶體中。或者,如果頁面已有一段時間沒有被存取,則可能會將頁面的壓縮形式寫入磁碟,以釋放空間給其他資料。因此,在任何給定時間,緩衝池可能同時包含頁面的壓縮和未壓縮形式,或者僅包含頁面的壓縮形式,或者兩者都不包含。

MySQL 使用最近最少使用 (LRU) 清單來追蹤要將哪些頁面保留在記憶體中以及要逐出哪些頁面,以便熱門(經常存取)資料傾向於保留在記憶體中。當存取壓縮表格時,MySQL 會使用適應性 LRU 演算法,以在記憶體中達到壓縮和未壓縮頁面的適當平衡。此適應性演算法會感應系統是以I/O 限制還是CPU 限制的方式執行。目標是避免在 CPU 忙碌時花費過多處理時間解壓縮頁面,並避免在 CPU 有可用於解壓縮壓縮頁面(可能已在記憶體中)的閒置週期時執行過多的 I/O。當系統受 I/O 限制時,該演算法傾向於逐出頁面的未壓縮副本,而不是兩個副本都逐出,以便為其他磁碟頁面成為駐留在記憶體中騰出更多空間。當系統受 CPU 限制時,MySQL 傾向於逐出壓縮和未壓縮的頁面,以便將更多記憶體用於熱門頁面,並減少僅以壓縮形式解壓縮記憶體中資料的需求。

壓縮和 InnoDB 重做日誌檔

在將壓縮頁面寫入資料檔之前,MySQL 會將頁面的副本寫入重做日誌(如果自上次寫入資料庫以來已重新壓縮)。這樣做是為了確保重做日誌可用於損毀復原,即使在不太可能發生的情況下,升級了 zlib 程式庫,並且該變更會導致壓縮資料出現相容性問題。因此,使用壓縮時,預計會增加日誌檔的大小,或需要更頻繁的檢查點。日誌檔大小增加或檢查點頻率增加的幅度,取決於以需要重組和重新壓縮的方式修改壓縮頁面的次數。

若要在單表檔案表空間中建立壓縮表格,必須啟用innodb_file_per_table。在一般表空間中建立壓縮表格時,不依賴innodb_file_per_table 設定。如需更多資訊,請參閱第 17.6.3.3 節「一般表空間」