#### 15.9.1.3 调整 InnoDB 表的压缩

[](<>)[](<>)

大多数情况下，描述的内部优化[InnoDB 数据存储和压缩](innodb-compression-internals.html#innodb-compression-internals-storage)确保系统使用压缩数据运行良好。但是，由于压缩效率取决于数据的性质，因此您可以做出影响压缩表性能的决策：

-   要压缩哪些表。

-   使用什么压缩页面大小。

-   是否根据运行时性能特征调整缓冲池的大小，例如系统压缩和解压缩数据所花费的时间。工作量是否更像是一个[数据仓库](glossary.html#glos_data_warehouse)（主要是查询）或[OLTP](glossary.html#glos_oltp)系统（查询和[DML](glossary.html#glos_dml)）。

-   如果系统对压缩表进行 DML 操作，数据的分布方式导致代价高昂[压缩失败](glossary.html#glos_compression_failure)在运行时，您可能会调整其他高级配置选项。

    使用本节中的指南来帮助做出这些架构和配置选择。当您准备好进行长期测试并将压缩表投入生产时，请参阅[第 15.9.1.4 节，“在运行时监控 InnoDB 表压缩”](innodb-compression-tuning-monitoring.html)寻找在现实条件下验证这些选择的有效性的方法。

##### 何时使用压缩

通常，压缩在包含合理数量的字符串列且数据读取频率远高于写入频率的表上效果最佳。因为没有可靠的方法来预测压缩是否有益于特定情况，所以总是使用特定的测试[工作量](glossary.html#glos_workload)和在代表性配置上运行的数据集。在决定要压缩哪些表时，请考虑以下因素。

##### 数据特征和压缩

[](<>)

压缩效率在减小数据文件大小方面的一个关键决定因素是数据本身的性质。回想一下，压缩是通过识别数据块中重复的字节串来工作的。完全随机的数据是最坏的情况。典型数据通常具有重复值，因此可以有效压缩。字符串通常可以很好地压缩，无论是在`字符`,`VARCHAR`,`文本`要么`斑点`列。另一方面，主要包含二进制数据（整数或浮点数）或先前压缩的数据（例如 JPEG 或 PNG 图像）的表通常可能无法很好地、显着地或根本无法压缩。

您选择是否为每个 InnoDB 表打开压缩。一个表和它的所有索引都使用相同的（压缩的）[页面大小](glossary.html#glos_page_size).这可能是[首要的关键](glossary.html#glos_primary_key)（聚集）索引包含表的所有列的数据，比二级索引更有效地压缩。对于存在长行的情况，使用压缩可能会导致长列值“离页”存储，如在[动态行格式](innodb-row-format.html#innodb-row-format-dynamic).那些溢出页面可能会很好地压缩。考虑到这些考虑因素，对于许多应用程序而言，某些表的压缩效率高于其他表，并且您可能会发现您的工作负载仅在压缩表的子集时表现最佳。

要确定是否压缩特定表，请进行实验。通过使用实现 LZ77 压缩的实用程序（例如`压缩包`或 WinZip) 的副本上[.ibd 文件](glossary.html#glos_ibd_file)对于未压缩的表。与基于文件的压缩工具相比，您可以期望 MySQL 压缩表的压缩更少，因为 MySQL 基于块压缩数据[页面大小](glossary.html#glos_page_size), 默认为 16KB。除了用户数据之外，页面格式还包括一些未压缩的内部系统数据。基于文件的压缩实用程序可以检查更大的数据块，因此可能会在大文件中找到比 MySQL 在单个页面中找到的更多重复字符串。

在特定表上测试压缩的另一种方法是将未压缩表中的一些数据复制到一个类似的压缩表（具有所有相同的索引）中[每表文件](glossary.html#glos_file_per_table)表空间并查看结果的大小`.ibd`文件。例如：

```
USE test;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL autocommit=0;

-- Create an uncompressed table with a million or two rows.
CREATE TABLE big_table AS SELECT * FROM information_schema.columns;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
INSERT INTO big_table SELECT * FROM big_table;
COMMIT;
ALTER TABLE big_table ADD id int unsigned NOT NULL PRIMARY KEY auto_increment;

SHOW CREATE TABLE big_table\G

select count(id) from big_table;

-- Check how much space is needed for the uncompressed table.
\! ls -l data/test/big_table.ibd

CREATE TABLE key_block_size_4 LIKE big_table;
ALTER TABLE key_block_size_4 key_block_size=4 row_format=compressed;

INSERT INTO key_block_size_4 SELECT * FROM big_table;
commit;

-- Check how much space is needed for a compressed table
-- with particular compression settings.
\! ls -l data/test/key_block_size_4.ibd
```

该实验产生了以下数字，当然，根据您的表结构和数据，这些数字可能会有很大差异：

```
-rw-rw----  1 cirrus  staff  310378496 Jan  9 13:44 data/test/big_table.ibd
-rw-rw----  1 cirrus  staff  83886080 Jan  9 15:10 data/test/key_block_size_4.ibd
```

查看压缩是否对您的特定内容有效[工作量](glossary.html#glos_workload)：

-   对于简单的测试，使用没有其他压缩表的 MySQL 实例并针对[`INFORMATION_SCHEMA.INNODB_CMP`](information-schema-innodb-cmp-table.html)桌子。

-   对于涉及具有多个压缩表的工作负载的更详细的测试，请针对[`INFORMATION_SCHEMA.INNODB_CMP_PER_INDEX`](information-schema-innodb-cmp-per-index-table.html)桌子。因为统计中的`INNODB_CMP_PER_INDEX`收集表很昂贵，您必须启用配置选项[`innodb_cmp_per_index_enabled`](innodb-parameters.html#sysvar_innodb_cmp_per_index_enabled)在查询该表之前，您可能会将此类测试限制在开发服务器或非关键副本服务器上。

-   针对您正在测试的压缩表运行一些典型的 SQL 语句。

-   通过查询成功的压缩操作与整体压缩操作的比率来检查[`INFORMATION_SCHEMA.INNODB_CMP`](information-schema-innodb-cmp-table.html)要么[`INFORMATION_SCHEMA.INNODB_CMP_PER_INDEX`](information-schema-innodb-cmp-per-index-table.html)表和比较`压缩_OPS`到`COMPRESS_OPS_OK`.

-   如果压缩操作成功完成的百分比很高，则该表可能是一个很好的压缩候选者。

-   如果你得到的比例很高[压缩失败](glossary.html#glos_compression_failure), 你可以调整[`innodb_compression_level`](innodb-parameters.html#sysvar_innodb_compression_level),[`innodb_compression_failure_threshold_pct`](innodb-parameters.html#sysvar_innodb_compression_failure_threshold_pct)， 和[`innodb_compression_pad_pct_max`](innodb-parameters.html#sysvar_innodb_compression_pad_pct_max)如中所述的选项[第 15.9.1.6 节，“OLTP 工作负载的压缩”](innodb-performance-compression-oltp.html)，并尝试进一步的测试。

##### 数据库压缩与应用程序压缩

[](<>)

决定是在应用程序中还是在表中压缩数据；不要对同一数据使用两种类型的压缩。当您在应用程序中压缩数据并将结果存储在压缩表中时，极不可能节省额外的空间，双重压缩只会浪费 CPU 周期。

##### 在数据库中压缩

启用后，MySQL 表压缩是自动的，并适用于所有列和索引值。这些列仍然可以使用运算符进行测试，例如`喜欢`, 即使索引值被压缩，排序操作仍然可以使用索引。由于索引通常占数据库总大小的很大一部分，因此压缩可以显着节省存储、I/O 或处理器时间。压缩和解压缩操作发生在数据库服务器上，这可能是一个强大的系统，其大小可以处理预期的负载。

##### 在应用程序中压缩

如果您在应用程序中压缩诸如文本之类的数据，则在将其插入数据库之前，您可以通过压缩某些列而不是其他列来节省无法很好压缩的数据的开销。这种方法在客户机而不是数据库服务器上使用 CPU 周期进行压缩和解压缩，这可能适用于具有许多客户机的分布式应用程序，或者客户机具有空闲 CPU 周期的情况。

##### 混合方法

当然，可以将这些方法结合起来。对于某些应用程序，使用一些压缩表和一些未压缩表可能是合适的。最好在外部压缩一些数据（并将其存储在未压缩的表中）并允许 MySQL 压缩（部分）应用程序中的其他表。与往常一样，前期设计和实际测试对于做出正确决策很有价值。

##### 工作负载特征和压缩

[](<>)

除了选择要压缩的表（和页面大小）之外，工作负载是性能的另一个关键决定因素。如果应用程序以读取而不是更新为主，那么在索引页用完 MySQL 为压缩数据维护的每页“修改日志”空间后，需要重新组织和重新压缩的页面就会更少。如果更新主要更改非索引列或包含`斑点`s 或碰巧存储在“页外”的大字符串，压缩的开销可能是可以接受的。如果对表的唯一更改是`插入`使用单调递增的主键，二级索引很少，几乎不需要重新组织和重新压缩索引页。由于 MySQL 可以通过修改未压缩数据“就地”“删除标记”和删除压缩页面上的行，`删除`对表的操作相对有效。

对于某些环境，加载数据所需的时间可能与运行时检索一样重要。尤其是在数据仓库环境中，许多表可能是只读的或只读的。在这些情况下，为增加加载时间而付出压缩的代价可能是可接受的，也可能是不可接受的，除非由此带来的磁盘读取次数减少或存储成本的节省是显着的。

从根本上说，当 CPU 时间可用于压缩和解压缩数据时，压缩效果最好。因此，如果您的工作负载受 I/O 限制，而不是 CPU 限制，您可能会发现压缩可以提高整体性能。当您使用不同的压缩配置测试您的应用程序性能时，请在类似于生产系统的计划配置的平台上进行测试。

##### 配置特征和压缩

[](<>)

读写数据库[页面](glossary.html#glos_page)从磁盘到磁盘是系统性能最慢的方面。压缩尝试通过使用 CPU 时间来压缩和解压缩数据来减少 I/O，并且在 I/O 与处理器周期相比是相对稀缺的资源时最有效。

在具有快速多核 CPU 的多用户环境中运行时，通常尤其如此。当压缩表的一页在内存中时，MySQL 经常在内存中使用额外的内存，通常为 16KB。[缓冲池](glossary.html#glos_buffer_pool)用于页面的未压缩副本。自适应 LRU 算法尝试平衡压缩和未压缩页面之间的内存使用，以考虑工作负载是以 I/O 密集型还是 CPU 密集型方式运行。尽管如此，与内存高度受限的配置相比，使用压缩表时，具有更多专用于缓冲池的内存的配置往往运行得更好。

##### 选择压缩页面大小

[](<>)[](<>)[](<>)[](<>)

压缩页面大小的最佳设置取决于表及其索引包含的数据的类型和分布。压缩页面大小应始终大于最大记录大小，否则操作可能会失败，如[B-Tree 页面的压缩](innodb-compression-internals.html#innodb-compression-internals-storage-btree).

将压缩页面大小设置得太大会浪费一些空间，但不必经常压缩页面。如果压缩页面大小设置得太小，插入或更新可能需要耗时的重新压缩，并且[B树](glossary.html#glos_b_tree)节点可能必须更频繁地拆分，从而导致更大的数据文件和更低的索引效率。

通常，您将压缩页面大小设置为 8K 或 4K 字节。鉴于 InnoDB 表的最大行大小约为 8K，`KEY_BLOCK_SIZE=8`通常是一个安全的选择。
