InnoDB 表和页面压缩

学习参考文献:
https://dev.mysql.com/doc/refman/5.7/en/innodb-compression.html

使用 的压缩功能InnoDB,您可以创建以压缩形式存储数据的表。压缩有助于提高原始性能和可扩展性。压缩意味着在磁盘和内存之间传输的数据更少,占用的磁盘和内存空间也更少。对于具有 二级索引的表,其优势更加明显,因为索引数据也被压缩了。压缩对于SSD存储设备尤其重要,因为它们的容量往往低于 HDD设备。压缩分两种:InnoDB表压缩和InnoDB页面压缩

1、表压缩

1.1 概述

由于处理器和缓存的速度比磁盘存储设备快得多,因此许多工作负载都与磁盘绑定。数据压缩可以缩小数据库大小、减少 I/O 并提高吞吐量,但代价是增加 CPU 利用率。InnoDB使用ROW_FORMAT=COMPRESSED创建的表在磁盘可以使用比配置值更小的页面大小innodb_page_size。

可以使用CREATE TABLEor ALTER TABLE KEY_BLOCK_SIZE指定压缩的页大小,不同的页面大小要求将表放在文件表空间或通用表空间中,而不是系统表空间中,因为系统表空间无法存储压缩表。

压缩级别与 KEY_BLOCK_SIZE 值无关。指定较小的 KEY_BLOCK_SIZE 值可以获得更小页面带来的 I/O 优势。然而,如果指定的值过小,当数据值无法压缩到足够小以适合每页多个行时,就会产生额外的开销来重新组织页面。对于每个索引的键列长度,表的 KEY_BLOCK_SIZE 有一个最小限制。如果指定的值过小,则 CREATE TABLE 或 ALTER TABLE 语句会失败。

在缓冲池中,压缩数据也会存在。为了提取或更新列值,MySQL 还会在缓冲池中创建一个包含未压缩数据的未压缩页面。在缓冲池中,对未压缩页面的任何更新也会重新写回到等效的压缩页面。您可能需要调整缓冲池的大小以容纳压缩和未压缩页面的额外数据,尽管在需要空间时未压缩页面会从缓冲池中逐出,然后在下次访问时再次解压缩。

1.2 创建压缩表

压缩表可以在 文件表表空间或 通用表空间中 创建 。InnoDB系统表空间不提供表压缩。系统表空间(空间 0,.ibdata 文件)可以包含用户创建的表,但它还包含内部系统数据,这些数据永远不会被压缩。因此,压缩仅适用于存储在 file-per-table 或通用表空间中的表(和索引)。

在文件表空间中创建压缩表
要在每个表文件表空间中创建压缩表, innodb_file_per_table必须启用(MySQL 5.6.6 中的默认值)并且 innodb_file_format必须设置为Barracuda。您可以在 MySQL 配置文件(my.cnf或 my.ini)中设置这些参数,也可以使用语句动态 设置这些参数SET 。配置完innodb_file_per_table和 innodb_file_format这两个选项之后,需要在创建表CREATE TABLE或者更新ALTER TABLE语句的时候指定【ROW_FORMAT=COMPRESSED、KEY_BLOCK_SIZE】其中一个或者两个都配置。

SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_file_format=Barracuda;
CREATE TABLE t1
 (c1 INT PRIMARY KEY)
 ROW_FORMAT=COMPRESSED
 KEY_BLOCK_SIZE=8;

在通用表表空间中创建压缩表
要在通用表空间中创建压缩表, FILE_BLOCK_SIZE必须为通用表空间定义,该值在创建表空间时指定。FILE_BLOCK_SIZE与innodb_page_size和压缩表的page size【由CREATE TABLEor ALTER TABLE KEY_BLOCK_SIZE设置的,并且为FILE_BLOCK_SIZE/1024】相关。

以下示例演示了如何创建通用表空间并添加压缩表。示例假设默认值为 innodb_page_size16K。 FILE_BLOCK_SIZE是8192,要求压缩表的值为KEY_BLOCK_SIZE=8。

mysql> CREATE TABLESPACE `ts2` ADD DATAFILE 'ts2.ibd' FILE_BLOCK_SIZE = 8192 Engine=InnoDB;

mysql> CREATE TABLE t4 (c1 INT PRIMARY KEY) TABLESPACE ts2 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;

note:

  • 如果指定了ROW_FORMAT=COMPRESSED,则可以省略KEY_BLOCK_SIZE; KEY_BLOCK_SIZE设置默认为innodb_page_size值的一半;
  • 如果您指定了有效值KEY_BLOCK_SIZE ,则可以省略 ROW_FORMAT=COMPRESSED;压缩会自动启用;
  • 要确定KEY_BLOCK_SIZE的最佳值,通常需要为同一个表创建多个副本,并为此子句指定不同的值,然后测量生成的.ibd文件的大小,并查看每个文件在实际工作负载下的性能 。对于通用表空间,请记住删除表不会减小通用表空间文件的大小,也不会将磁盘空间返回给操作系统;
  • KEY_BLOCK_SIZE 值被视为一个提示;如果需要,InnoDB 可以使用不同的大小。对于每表文件表空间(file-per-table tablespaces),KEY_BLOCK_SIZE 只能小于或等于 innodb_page_size 值。如果指定的值大于 innodb_page_size,系统将忽略指定的值并发出警告,同时将 KEY_BLOCK_SIZE 设置为 innodb_page_size 值的一半。如果 innodb_strict_mode=ON,则指定无效的 KEY_BLOCK_SIZE 值会返回错误。对于通用表空间(general tablespaces),有效的 KEY_BLOCK_SIZE 值取决于表空间的 FILE_BLOCK_SIZE 设置;
  • 页面大小page size为32k、64k的,不支持压缩;
  • InnoDB 数据页的默认未压缩大小为 16KB。针对于文件表空间,根据选项值的组合,MySQL 会使用 1KB、2KB、4KB、8KB 或 16KB 的页大小(.ibd 文件)。实际的压缩算法不受 KEY_BLOCK_SIZE 值的影响;该值决定了每个压缩块的大小,而这反过来会影响每个压缩页能够容纳多少行数据【每个压缩页面(compressed page)中可以包含多少行数据】;
  • 针对于文件表空间中的压缩,如果KEY_BLOCK_SIZE设置的大小等于page size的话,那么压缩不了多少,除非针对于long BLOB, VARCHAR or TEXT 这种列。针对于通用表空间压缩,不允许设置KEY_BLOCK_SIZE的大小等于page size。

1.3 调整 InnoDB 表的压缩

1.3.1 何时使用压缩

一般而言,压缩特别适用那种列是字符串,并且是读多写少的表。由于没有很好的方式能够确保压缩对某种特定情况的好坏,所以可以在特定的负载下使用数据进行压测。

1.3.2 数据特征和压缩

压缩在减小数据文件大小方面的效率的一个关键决定因素是数据本身的性质。回想一下,压缩是通过识别数据块中重复的字节串来实现的。完全随机的数据是最坏的情况。典型的数据通常具有重复的值,因此压缩效果很好。字符串【无论使用CHAR、VARCHAR、TEXT或者是BLOB中定义的】通常压缩效果很好。另一方面,主要包含二进制数据(整数或浮点数)或先前压缩过的数据(例如 JPEG 或 PNG 图像)的表通常可能压缩效果不佳、压缩效果不明显或根本无法压缩。

您可以选择是否为每个 InnoDB 表启用压缩。表及其所有索引使用相同(压缩)的 页面大小。包含表所有列的数据的主键(聚集)索引可能 比二级索引压缩得更有效。

要确定是否要压缩特定表【使用压缩看下压缩比】,请进行实验。

  • 方案一:您可以使用实现了 LZ77 压缩算法的实用程序(例如或 WinZip)对未压缩表的.ibd 文件副本进行gzip压缩,粗略估计数据的压缩效率。MySQL 压缩表的压缩效果可能比基于文件的压缩工具要差,因为 MySQL 根据页面大小(默认为 16KB)分块压缩数据。除了用户数据之外,页面格式还包括一些没有被压缩的内部系统数据。并且基于文件的压缩实用程序可以检查更大的数据块,因此可能会在大型文件中找到比 MySQL 在单个页面中找到的更多的重复字符串;
  • 方案二:将一些数据从未压缩的表中复制到文件文件表空间中的类似压缩表(具有所有相同的索引),然后查看生成的文件的大小 .ibd;
USE test;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_file_format=Barracuda;
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

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

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

在数据库中压缩
启用后,MySQL表压缩是自动的,并适用于所有列和索引值。仍然可以使用诸如LIKE之类的运算符,并且即使索引值被压缩,排序操作仍然可以使用索引。由于索引通常占数据库总大小的很大一部分,因此压缩可以大大节省存储、I/O 或处理器时间。压缩和解压缩操作发生在数据库服务器上,该服务器可能是一个功能强大的系统,其大小足以处理预期的负载。

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

混合方法
当然,可以结合这些方法。对于某些应用程序,使用一些压缩表和一些未压缩表可能是合适的。最好从外部压缩一些数据(并将其存储在未压缩的表中),并允许 MySQL 压缩应用程序中的其他表。一部分表进行Mysql压缩,一部分表进行应用程序压缩。

工作负载特征和压缩
除了选择要压缩哪些表(以及页面大小)之外,工作负载是决定性能的另一个关键因素。

  • 如果应用程序以读取而非更新为主,则很少因为在索引页用尽 MySQL 为压缩数据维护的每页“modification log”的空间后,需要重新组织和重新压缩的页面。

  • 如果更新,主要更改非索引列或着恰好存储在“页外”的BLOB或大字符串的列,则压缩的开销可能是可以接受的。

  • 如果对表的唯一更改是使用单调递增主键的INSERT,并且二级索引很少,则几乎不需要重新组织和重新压缩索引页。

  • 由于 MySQL 可以通过修改未压缩的数据对压缩页面上的行进行“删除标记”并“就地”删除行,因此对表的 DELETE 操作相对高效。

对于某些环境,加载数据所需的时间可能与运行时检索一样重要。特别是在数据仓库环境中,许多表可能是只读的或大部分是读的。在这些情况下,以增加加载时间为代价进行压缩可能是可以接受的或不可接受,除非由此带来的磁盘读取次数减少或存储成本的节省是显著的。
从根本上讲,当 CPU 时间可用于压缩和解压缩数据时,压缩效果最佳。因此,如果您的工作负载是 I/O 密集型的,而不是 CPU 密集型的,您可能会发现压缩可以提高整体性能。

选择压缩页面大小
压缩页面大小的最佳设置取决于表及其索引所含数据的类型和分布。压缩页面大小应始终大于最大记录大小,否则操作可能会失败。将压缩页面大小设置得太大会浪费一些空间,但页面不必经常压缩。如果将压缩页面大小设置得太小,插入或更新可能需要耗时的重新压缩,并且 B 树节点可能需要更频繁地拆分,从而导致数据文件更大,索引效率更低。
通常,将压缩页面大小设置为 8K 或 4K 字节。考虑到 InnoDB 表的最大行大小约为 8K,这KEY_BLOCK_SIZE=8通常是一个安全的选择。

1.4 运行时监控InnoDB表压缩

INNODB_CMP:表报告了正在使用的每个压缩页面大小 ( KEY_BLOCK_SIZE) 的压缩活动信息。这些表中的信息是系统范围的:它总结了数据库中所有压缩表的压缩统计信息。您可以使用这些数据来帮助决定是否压缩表,方法是在没有其他压缩表被访问时检查这些表。它对服务器的开销相对较低,因此您可以在生产服务器上定期查询它以检查压缩功能的整体效率。

INNODB_CMP_PER_INDEX:表报告有关各个表和索引的压缩活动的信息。此信息更有针对性,更适合于评估压缩效率和诊断一个表或索引的性能问题。(由于每个InnoDB表都表示为聚集索引,因此 MySQL 不会在此上下文中对表和索引进行很大的区分。)该 INNODB_CMP_PER_INDEX表确实涉及大量开销,因此它更适合开发服务器,您可以在其中单独比较不同工作负载、数据和压缩设置的影响。为了防止意外施加此监控开销,您必须先启用 innodb_cmp_per_index_enabled 配置选项,然后才能查询 INNODB_CMP_PER_INDEX表。

需要考虑的关键统计数据是执行压缩和解压缩操作的数量和时间。由于 MySQL在B 树 节点太满而无法容纳修改后的压缩数据时会拆分它们,因此请将 “成功”压缩操作的数量与此类操作的总数进行比较。根据 INNODB_CMP和 INNODB_CMP_PER_INDEX表中的信息以及总体应用程序性能和硬件资源利用率,您可以更改硬件配置、调整缓冲池的大小、选择不同的页面大小或选择要压缩的另一组表。

如果压缩和解压缩所需的 CPU 时间很长,则更换​​为更快或多核 CPU 有助于在数据、应用程序工作负载和压缩表集相同的情况下提高性能。增加缓冲池的大小也可能有助于提高性能,这样更多未压缩的页面就可以保留在内存中,从而减少对仅以压缩形式存在于内存中的页面进行解压缩的需要。

与应用程序中的插入、更新和删除操作数量以及数据库的大小相比,压缩操作数量较大可能表明某些压缩表的更新过于频繁,导致压缩效果不佳。在这种情况下,可以选择更大的页面大小,或者更精选地决定哪些表需要压缩。

如果“成功”压缩操作 的数量( COMPRESS_OPS_OK) 占压缩操作总数 ( COMPRESS_OPS) 的百分比很高,则系统可能运行良好。如果该比率较低,则 MySQL 重组、重新压缩和拆分 B 树节点的频率比预期的要高。在这种情况下,请避免压缩某些表,或增加KEY_BLOCK_SIZE某些压缩表的压缩次数。您可以关闭导致应用程序中“压缩失败”数量超过总数 1% 或 2% 的表的压缩。(在数据加载等临时操作期间,这样的失败率可能是可以接受的)。

1.5 InnoDB表的压缩工作原理

压缩算法

MySQL 借助著名的 zlib 库实现压缩,该库实现了 LZ77 压缩算法。这种压缩算法成熟、可靠,在 CPU 利用率和数据大小减少方面都很高效。该算法是 “无损”的,因此始终可以从压缩形式重建原始未压缩数据。LZ77 压缩的工作原理是查找要压缩的数据中重复的数据序列。数据中值的模式决定了压缩效果,但典型的用户数据通常压缩 50% 或更多。

在 MySQL 5.7.24 之前版本,InnoDB支持 zlib最高版本 1.2.3 的库。在 MySQL 5.7.24 及更高版本中,InnoDB支持 zlib最高版本 1.2.11 的库。

与应用程序执行的压缩或某些其他数据库管理系统的压缩功能不同,InnoDB 压缩既适用于用户数据,也适用于索引。在许多情况下,索引可能占总数据库大小的 40-50% 或更多,因此这种差异非常重要。当压缩对数据集产生良好效果时,InnoDB 数据文件( 每个表的文件表空间或通用表空间 .ibd文件)的大小是未压缩大小的 25% 到 50% 或可能更小。根据工作负载,这个较小的数据库反过来可以减少 I/O 并增加吞吐量,而 CPU 利用率的增加则只是适度的代价。您可以通过修改配置选项来调整压缩级别和 CPU 开销之间的平衡 innodb_compression_level 。

InnoDB数据存储和压缩
InnoDB 表中的所有用户数据都存储在包含 B 树索引( 聚集索引)的页面中。在其他一些数据库系统中,这种类型的索引称为 “索引组织表” 。索引节点中的每一行都包含(用户指定或系统生成的)主键和表的所有其他列 的值 。
InnoDB 表中的二级索引也是 B 树,包含一对值:索引键和指向聚簇索引中某行的指针。该指针实际上是表的主键值,用于在需要访问除索引键和主键以外的其他列时访问聚簇索引。二级索引记录必须始终适合一个单独的 B 树页。
聚簇索引和二级索引的 B 树节点的压缩方式与用于存储长 VARCHAR、BLOB 或 TEXT 列的溢出页面的压缩方式不同,即页内的压缩方式不同于页外的压缩方式。

B-Tree页面压缩
由于 B 树页面更新频繁,因此需要特殊处理。尽量减少 B 树节点拆分的次数,以及尽量减少对其内容进行解压和重新压缩的需要,这一点很重要。MySQL 使用的一种技术是将一些系统信息以未压缩的形式保存在 B 树节点中,从而便于执行某些就地更新。例如,这允许对行进行删除标记和删除,而无需执行任何压缩操作。此外,MySQL 还尝试避免在索引页发生更改时进行不必要的解压缩和重新压缩。在每个 B 树页中,系统都会保留一个未压缩的 “modification log”来记录对页面所做的更改。小记录的更新和插入可以写入此修改日志,而无需完全重建整个页面。当修改日志的空间用完时,InnoDB 会解压页面,应用更改并重新压缩页面。如果重新压缩失败(这种情况称为 压缩失败),则 B 树节点将被拆分,并重复该过程,直到更新或插入成功。为了避免在写入密集型工作负载(例如OLTP 应用程序)中频繁出现压缩失败,MySQL 有时会在页面中保留一些空白空间(填充),以便修改日志更快地填满,并在仍有足够空间避免页面拆分时重新压缩页面。由于系统会跟踪页面拆分的频率,因此每个页面中剩余的填充空间量会有所不同。在对压缩表进行频繁写入的繁忙服务器上,您可以调整 innodb_compression_failure_threshold_pct、 和 innodb_compression_pad_pct_max 配置选项来微调此机制。

通常,MySQL 要求 InnoDB 表中的每个 B-tree 页面至少能容纳两个记录。对于压缩表,这一要求已经放宽。B-tree 节点的叶子页面(无论是主键还是二级索引)只需容纳一个记录,但该记录必须能够以未压缩的形式适应每页面的修改日志。如果 innodb_strict_mode 处于开启状态,MySQL 在创建表或创建索引时会检查最大行大小。如果行不适合,将会发出以下错误消息:ERROR HY000: Too big row。如果在 innodb_strict_mode 关闭时创建表,而后续的 INSERT 或 UPDATE 语句尝试创建一个不适合压缩页面大小的索引条目,该操作将失败,并返回错误:ERROR 42000: Row size too large。(此错误消息不会指出哪个索引的记录过大,也不会提到索引记录的长度或该特定索引页面的最大记录大小。)要解决此问题,可以通过 ALTER TABLE 重建表并选择更大的压缩页面大小(KEY_BLOCK_SIZE),缩短任何列前缀索引,或通过 ROW_FORMAT=DYNAMIC 或 ROW_FORMAT=COMPACT 完全禁用压缩。

压缩 BLOB、VARCHAR 和 TEXT 列
在 InnoDB 表中,BLOB、VARCHAR 和 TEXT 列如果不是主键的一部分,可能会存储在单独分配的溢出页面上。我们将这些列称为离页列。它们的值存储在单链表的溢出页面中。

对于以 ROW_FORMAT=DYNAMIC 或 ROW_FORMAT=COMPRESSED 创建的表,BLOB、TEXT 或 VARCHAR 列的值可以根据其长度和整行的长度完全存储在离页区。对于存储在离页的列,聚簇索引记录仅包含指向溢出页面的 20 字节指针,每列一个。是否将列存储在离页取决于页面大小和整行的总大小。当一行太长而无法完全适应聚簇索引页面时,MySQL 会选择最长的列进行离页存储,直到该行可以适应聚簇索引页面。如上所述,如果一行不能在压缩页面上完全适应,就会发生错误。

在旧版本的 MySQL 中创建的表使用 Antelope 文件格式,该格式仅支持 ROW_FORMAT=REDUNDANT 和 ROW_FORMAT=COMPACT。在这些格式中,MySQL 将 BLOB、VARCHAR 和 TEXT 列的前 768 字节存储在聚簇索引记录中,连同主键一起。768 字节的前缀后面是指向存储其余列值的溢出页面的 20 字节指针。

当表处于压缩格式时,所有写入溢出页面的数据都以“原样”压缩;也就是说,MySQL 将 zlib 压缩算法应用于整个数据项。除了数据之外,压缩的溢出页面还包含一个未压缩的头部和尾部,其中包括页面校验和和指向下一个溢出页面的链接等。因此,对于较长的 BLOB、TEXT 或 VARCHAR 列,如果数据具有高度可压缩性(例如文本数据),可以获得显著的存储节省。然而,图像数据(如 JPEG)通常已被压缩,因此在压缩表中存储时并不会获得太多好处;双重压缩可能会浪费 CPU 资源,几乎没有空间节省。

溢出页面的大小与其他页面相同。包含十列离页存储的行占用十个溢出页面,即使这些列的总长度仅为 8K 字节。在一个未压缩的表中,十个未压缩的溢出页面占用 160K 字节。在一个具有 8K 页面大小的压缩表中,它们仅占用 80K 字节。因此,对于具有长列值的表,使用压缩表格式通常更为高效。

对于每表文件表空间,使用 16K 的压缩页面大小可以降低 BLOB、VARCHAR 或 TEXT 列的存储和 I/O 成本,因为这些数据通常易于压缩,因此可能需要更少的溢出页面,尽管 B 树节点本身所需的页面数量与未压缩形式相同。通用表空间不支持 16K 压缩页面大小(KEY_BLOCK_SIZE)。

压缩和 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 redo log文件
在压缩页面被写入数据文件之前,MySQL 会将页面的副本写入重做日志(前提是自上次写入数据库以来该页面已被重新压缩)。这样做是为了确保重做日志在崩溃恢复时可用,即使不太可能发生的情况下,zlib 库的升级引入了与压缩数据的兼容性问题。因此,使用压缩时可以预期日志文件的大小会有所增加,或者需要更频繁的检查点。日志文件大小或检查点频率的增加量取决于压缩页面以需要重新组织和重新压缩的方式被修改的次数。

1.6 OLTP 工作负载的压缩

MySQL 5.6 中引入的配置选项允许您调整特定 MySQL 实例的压缩工作方式,重点关注写密集型操作的性能和可扩展性:

  • innodb_compression_level 可让您调高或调低压缩程度。较高的值可让您将更多数据放入存储设备,但压缩期间的 CPU 开销会更大。较低的值可在存储空间不重要或您认为数据不是特别可压缩时减少 CPU 开销。

  • innodb_compression_failure_threshold_pct指定在更新压缩表期间压缩失败的截止点 。超过此阈值后,MySQL 开始在每个新的压缩页面中留出额外的可用空间,动态调整可用空间量,直至达到由以下函数指定的页面大小百分比: innodb_compression_pad_pct_max

  • innodb_compression_pad_pct_max允许您调整每个页面 中保留的最大空间量,以记录对压缩行的更改,而无需再次压缩整个页面。值越高,无需重新压缩页面即可记录的更改越多。仅当运行时指定百分比的压缩操作 “失败”时,MySQL 才会为每个压缩表中的页面使用可变数量的可用空间 ,从而需要昂贵的操作来拆分压缩页面。

  • innodb_log_compressed_pages可让您禁用将重新压缩页面的图像写入重做日志的功能 。 当对压缩数据进行更改时,可能会发生重新压缩。默认情况下启用此选项,以防止 在恢复期间使用不同版本的压缩算法时可能发生的损坏。如果您确定zlib版本不太可能更改,请禁用innodb_log_compressed_pages以减少修改压缩数据的工作负载的重做日志生成。

2、页面压缩

针对于文件表空间,InnoDB支持页面级别的压缩,此功能又被称为透明页压缩。页面压缩可以在CREATE TABLE或者 ALTER TABLE的时候指定COMPRESSION属性进行开启。支持的压缩算法包括ZLIB和LZ4.

2.1 页面压缩如何工作的?

当页面被写入时,它会使用指定的压缩算法进行压缩。压缩后的数据被写入磁盘,在该过程中,孔穿透机制会释放页面末尾的空块。如果压缩失败,数据将以未压缩的形式直接写出。页面压缩需要操作系统的打孔机制。InnoDB以InnoDB页为单元进行存储,对于一般的情况,InnoDB页默认为16KB,文件系统默认为4KB。当InnoDB要存储一个页面时,对16KB进行压缩,压缩后大小为12KB,那么12KB到16KB之间的内容会首先被填零,然后fallocate使用“释放”打洞,这样额外的一个文件系统块就因压缩而被节省了;同样,若压缩后的页面小于8KB或小于4KB,那么分别就可以节省8 KB 或 12 KB。文件打洞

2.2 启用页面压缩

要启用页面压缩,请在CREATE TABLE中指定COMPRESSION属性。例如:

CREATE TABLE t1 (c1 INT) COMPRESSION="zlib";

也可以使用ALTER TABLE语句进行压缩。然而,ALTER TABLE … COMPRESSION只是更新表空间压缩属性,在设置新的压缩算法后对表空间的写入将使用新的设置,但要将新的压缩算法应用于现有页面,必须使用 OPTIMIZE TABLE 重新构建表。

ALTER TABLE t1 COMPRESSION="zlib";
OPTIMIZE TABLE t1;

2.3 关闭页面压缩

为了关闭页面压缩,使用ALTER TABLE设置COMPRESSION=None。设置了COMPRESSION=None之后就不再进行页面压缩,为了解压已经存在的页面,在设置了COMPRESSION=None之后使用OPTIMIZE TABLE对表格进行重建

ALTER TABLE t1 COMPRESSION="None";
OPTIMIZE TABLE t1;

2.4 原理

透明页压缩虽然是最新特性,但是思想却十分简单,我认为其之所以新,也是因为利用了Linux punch hole的新特性。其大概思路就是在压缩时采用了写入文件后进行打洞操作、读入文件后进行解压操作。如下:
在这里插入图片描述
框图中的变换和逆变换可以对应加密解密、压缩解压等操作,这里肯定是指的压缩和解压操作了。这种思路简介明了,直接将压缩的工作移动到了文件操作这一层,和页的操作解除了耦合。

当然,这也有缺点,因为读入时就全部解压,写入时全部压缩,所以buffer pool中保有的缓存页都是未压缩的,所以相对于buffer pool中多数为压缩页的“压缩表格式“,可能会需要更大的buffer pool(内存)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥都想学的又啥都不会的研究生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值