Inonodb 表压缩分两种,官方文档上分别称为:InnoDB Table Compression(老压缩) 和 InnoDB Page Compression(透明表空间压缩),他们其实都是基于页(Page)的压缩。
InnoDB Table Compression
创建压缩表:
mysql> create table cyt (id int) row_format=compressed,key_block_size=4;
将一个已经存在的表修改为压缩表:
mysql> alter table cyt row_format=compressed,key_block_size=4;
虽然SQL语法中写的是 row_format=compressed ,但是压缩是针对页的,而不是记录。
类似 Oracle 的 OLTP 压缩,MySQL 并不是每次 DML 都要进行解压或压缩操作,设计上会尽可能地减少解压和压缩的次数。例如不解压也能插入数据,通过在剩余空间直接存放 redo log ,然后页空间存放满后,再解压,利用 redo log 更新完成后,最后再压缩存放(此时就没有redo log 了)。
启用了压缩,性能不一定会变差,一个优秀的压缩算法,甚至会让性能变得更好,因为在I/O Bound(IO密集型)的业务场景下,减少I/O操作的次数对性能提升比较明显。
压缩是将原来的页的数据通过压缩算法压缩到一定的大小,然后用 key_block_size 大小的页去存放。key_block_size 的设置并不影响是否压缩本身,只是确定压缩后的数据存放的页大小。所以,哪怕对 "innodb_page_size=16k 的数据"设置 key_block_size=16 也是可以压缩的,且效果还比较明显。key_block_size 的值通常设置为 innodb_page_size 的 1/2(经验值)是没有问题的,如果想研究不同的 key_block_size 下的压缩比例,可以通过 information_schema.INNODB_CMP 的 compress_ops_ok/compress_ops 来查看压缩比例。这个表里面的数据是累加的,是全局信息,没法对应到具体某一张表。如果想具体到某一张表,有如下两个办法:
方法一:
查询 INOODB_CMP_RESET,会把 INNODB_CMP 表中的数据复制过来,并清空 INNODB_CMP。然后只对某张表做一次压缩操作,由此可以计算出该表的压缩情况。
方法二:
将参数 innodb_cmp_per_index_enabled 设置为 1 (默认关闭,开启对性能有影响)
对表做了压缩操作后,就可以通过 INNODB_CMP_PER_INDEX 视图观察到某张表的压缩情况了。
压缩表不受 innodb_page_size 的影响,例如,假设 innodb_page_size=16K,我们创建通用表空间的块大小是 4K,当我们尝试在其上创建一张普通表时,会报错,说块大小不匹配,创建压缩表则可以成功:
create table cyt (id int) tablespace=generaltbs row_format=compressed, key_block_size=4;
InnoDB Page Compression
很简单,创建表的时候指定一个压缩算法即可:
mysql> create table cyt (it int) compression="zlib";
也可以修改已经存在的表:
mysql> alter table cyt compression="zlib";
mysql> optimize table cyt;
除了 zlib 算法,还可以使用 lz4 压缩算法,zlib 压缩率更高,lz4。
但是透明压缩需要操作系统(RHEL7 kernel >= 3.10.0-123)和文件系统(XFS,EXT4,NTFS 等)支持 Hole Punch,至于其它操作系统,请查阅官方文档。
透明压缩使用了文件系统(filesystem)层中稀疏文件的特性,来达到压缩的目的。例如:一个 16K 的块,压缩后变成了4K,在写入文件系统时调用 Punch holes 写入,实则只写入4K的数据,从innodb的角度看还是16K的页大小,SpaceID 和 PageNumber 的读取方式没有改变(细节由文件系统屏蔽),只是文件系统知道该页只需要4K就能够存储(对innodb是透明的)。
如果使用透明压缩创建表时提示 warning,show warnings 看到如下告警:
InnoDB: Punch hole not supported by the file system or the tablespace page size is not large enough. Compression disabled
除了告警提到的原因,还可能是内核版本太低不支持或者general方式安装的mysql不支持透明压缩,需要自己编译。
3.10.0-229.el7.x86_64 + MySQL 5.7.23 (general安装) 测试通过。