17.9.1.3 InnoDB 表压缩调整
通常情况下,InnoDB 数据存储和压缩中描述的内部优化确保了系统在使用压缩数据时运行良好。然而,因为压缩效率取决于您的数据性质,您可以根据需要影响压缩表性能的决定:
在本节中,您可以找到帮助做出这些架构和配置选择的指南。准备长期测试并将压缩表投入生产时,请查看第 17.9.1.4 节,“运行时监控 InnoDB 表压缩”以了解这些选择在实际情况下的有效性。
一般来说,压缩对包含合理数量字符字符串列的表格效果最好,并且数据被读取的频率远高于写入的频率。由于没有可以预测压缩是否对特定情况有效的方法,因此总是使用特定的工作负载和数据集在代表性配置下进行测试。考虑以下因素来决定哪些表格需要压缩。
压缩数据文件大小的效率关键因素是数据本身的性质。回忆压缩通过在数据块中识别重复字节串来工作。完全随机化的数据是最差的情况。典型数据通常具有重复值,因此可以有效地压缩。字符字符串通常可以很好地压缩,无论它们定义在CHAR
、VARCHAR
、TEXT
或BLOB
列中。另一方面,包含主要二进制数据(整数或浮点数)或已经压缩的数据(例如JPEG或PNG图像)的表格可能不太好地压缩,或者压缩效果很小甚至为零。
您可以选择为每个 InnoDB 表启用压缩。表和所有索引使用相同的(压缩)页面大小。可能的情况是,主键(聚簇)索引,因为它包含了表中的所有列数据,可以比次级索引更好地压缩。在那些有长行的案例中,使用压缩可能会导致长列值被存储在“off-page”中,如DYNAMIC Row Format所讨论的。这些溢出页面可能会压缩得很好。考虑到这些因素,对于许多应用程序,某些表可能比其他表更好地压缩,您可能发现您的工作负载在只压缩子集表时性能最佳。
要确定是否压缩特定表,请进行实验。您可以使用实现 LZ77 压缩算法的工具(如gzip
或 WinZip)对未压缩表的.ibd 文件进行压缩。您可以预期 MySQL 压缩表比文件基于压缩工具少一些,因为 MySQL 根据页面大小(默认 16KB)对数据进行压缩。此外,页面格式还包括一些内部系统数据,这些数据不被压缩。文件基于压缩工具可以检查更大的数据块,并因此可能在大文件中找到更多重复字符串,而 MySQL 在单个页面中找不到。
可以通过将某个表的数据从未压缩的表复制到一个具有相同索引的压缩表(在文件-per-表表空间中)并查看生成的.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
要确定是否对您的特定工作负载进行压缩:
-
对于简单的测试,可以使用没有其他压缩表的MySQL实例,并运行查询对信息架构
INNODB_CMP
表。 -
对于涉及多个压缩表的复杂测试,可以运行查询对信息架构
INNODB_CMP_PER_INDEX
表。由于INNODB_CMP_PER_INDEX
表中的统计信息很昂贵,因此在查询该表之前,您需要启用配置选项innodb_cmp_per_index_enabled
,并且可能将这种测试限制到开发服务器或非关键副本服务器上。 -
运行一些常见的SQL语句对压缩表进行测试。
-
通过查询
INFORMATION_SCHEMA.INNODB_CMP
或INFORMATION_SCHEMA.INNODB_CMP_PER_INDEX
,并将COMPRESS_OPS
与COMPRESS_OPS_OK
进行比较,以了解压缩操作的成功率。 -
如果压缩操作的成功率很高,该表可能是一个适合压缩的候选对象。
-
如果您遇到大量压缩失败,可以调整
innodb_compression_level
、innodb_compression_failure_threshold_pct
和innodb_compression_pad_pct_max
选项,详见第17.9.1.6节,“OLTP工作负载中的压缩”,然后进行进一步的测试。
决定是否在应用程序中或在表中压缩数据;不要对同一数据使用两种类型的压缩。将数据在应用程序中压缩并将结果存储在压缩表中时,额外的空间节省非常不可能,而双重压缩只是浪费CPU周期。
启用MySQL表压缩时,压缩将自动应用于所有列和索引值。这些列仍然可以使用操作符,如LIKE
进行测试,并且即使索引值被压缩,也可以继续使用索引来优化排序操作。由于索引通常是数据库总大小的重要组成部分,压缩可能会带来显著的存储、I/O或处理器时间节省。压缩和解压操作发生在数据库服务器上,这个服务器通常是一个强大的系统,可以轻松地处理预期的负载。
如果您在应用程序中对文本数据进行了压缩,然后将其插入到数据库中,您可能会通过只压缩一些列而不是所有列来避免不良压缩的开销。这种方法使用客户端机器的CPU周期来进行压缩和解压,而不是在数据库服务器上,这可能适用于分布式应用程序或客户端机器有闲置CPU周期的情况。
当然,也可以将这两种方法结合起来。对于某些应用程序,使用一些压缩表和一些未压缩表可能是最好的选择。在某些情况下,可能需要外部压缩一些数据(并将其存储在未压缩表中),然后让MySQL压缩其他表中的数据。正如总是如此,前期设计和实际测试对于达成正确的决策非常重要。
除了选择要压缩的表 (和页面大小) 之外,工作负载也是性能的关键因素。如果应用程序主要由读取操作组成,而不是更新操作,那么在索引页满了后不需要重新组织和重新压缩太多页面,因为 MySQL 对于压缩数据维护的修改日志空间有限。如果更新操作主要更改非索引列或包含BLOB
或大字符串的列,并且这些列存储在“off-page”中,那么压缩的开销可能是可接受的。如果表中的唯一变化是使用单调递增主键的INSERT
操作,并且没有太多次要索引,那么不需要重新组织和重新压缩索引页。由于 MySQL 可以在压缩页面上“in place”删除行,DELETE
操作对表来说相对高效。
对于一些环境,加载数据所需的时间可能与实时检索一样重要,特别是在数据仓库环境中,许多表可能是只读或读写少量。在这种情况下,可能需要支付压缩的代价,即增加加载时间,但如果压缩后的结果是减少磁盘读取次数或存储成本,那么这可能是可接受的。
从根本上讲,压缩在 CPU 时间可用时工作最好。因此,如果您的工作负载是 I/O 绑定的,而不是 CPU 绑定的,您可能会发现压缩可以改善整体性能。在测试应用程序性能时,使用与生产系统计划配置相似的平台进行测试。
从磁盘读取和写入数据库页面是系统性能中最慢的方面。压缩尝试通过使用 CPU 时间来压缩和解压数据,减少 I/O 操作,并且在 I/O 资源相对较scarce的情况下效果最好。
这尤其适用于多用户环境中运行的多核 CPU。当一个压缩表的页面位于内存中,MySQL 通常使用 buffer pool 中的 16KB 内存来存储该页面的未压缩副本。自适应 LRU 算法尝试平衡压缩和未压缩页面之间的内存使用,以考虑工作负载是否在 I/O- bound 或 CPU-bound 模式下运行。即使如此,一配置中 buffer pool 中的内存更多,使用压缩表时通常比内存严重受限的配置运行得更好。
压缩页面大小的最佳设置取决于表和索引中的数据类型和分布。压缩页面大小应该总是大于最大记录大小,以免操作失败,如B-Tree 页面压缩中所述。
如果压缩页面大小设置太大,会浪费一些空间,但这些页面不需要那么频繁地压缩。如果压缩页面大小设置太小,插入或更新操作可能需要时间-consuming 的重新压缩,并且 B-树结点可能需要更频繁地分裂,从而导致数据文件变大和索引效率下降。
通常,您将压缩页面大小设置为8KB或4KB字节。考虑到InnoDB表的最大行大小约为8KB,KEY_BLOCK_SIZE=8
通常是一个安全的选择。