从MySQL5.5.X版本开始支持InnoDB数据页压缩,数据页的压缩使数据文件体积变小,减少磁盘I/O,提高吞吐量,小成本地提高了CPU利用率。尤其是对读多写少的应用来说最为有效,同样的内存可以存储更多的数据,充分地“榨干”内存利用率。
它的工作原理是:当用户获取数据时,如果压缩的页没有在Innodb_Buffer_Pool缓冲池里,那么会从磁盘加载进去,并且会在Innodb_Buffer_Pool缓冲池里开辟一个新的未压缩的16 KB的数据页来解压缩,为了减少磁盘I/O以及对页的解压操作,在缓冲池里同时存在着被压缩的和未压缩的页。为了给其他需要的数据页腾出空间,缓冲池里会把未压缩的数据页踢出去,而保留压缩的页在内存中,如果未压缩的页在一段时间内没有被访问,那么会直接写入磁盘中,因此缓冲池中可能有压缩和未压缩的页,也可能只有压缩页。
InnoDB采用最近最少使用(LRU)算法,将经常被访问的热数据放入内存里。当访问一个压缩表时,InnoDB会通过自适应的LRU算法来实现内存中压缩页和未压缩页的平衡,其目的是避免当CPU繁忙时花费太多的时间在解压缩上,也是为了避免在CPU空闲时在解压缩操作上做过多的I/O操作。所以,当系统处于I/O瓶颈时,这个算法会踢出未压缩的页,而不是未压缩和压缩的页,从而为更多的页注入内存腾出空间。而当系统处于CPU瓶颈时,这个算法会同时踢出未压缩的页和压缩的页,留出更多的内存来存放热数据,减少解压缩带来的开销。
在以前的版本中,一个数据页是16 KB,现在可以在建表时指定压缩的页是1 KB、2 KB、4 KB还是8 KB,如果设置过小,会导致消耗更多的CPU,通常设置为8 KB。
注意,必须采用Barracuda文件格式且独立表空间,才支持数据页压缩,如下所示:
innodb_file_format = Barracuda
innodb_file_per_table = 1
要设置数据页为8 KB,在建表的时候加入ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8即可,代码如下:
CREATE TABLE 'compressed' (
'id' int(10) unsigned NOT NULL AUTO_INCREMENT,
'k' int(10) unsigned NOT NULL DEFAULT '0',
'c' char(120) NOT NULL DEFAULT '',
'pad' char(60) NOT NULL DEFAULT '',
PRIMARY KEY ('id'),
KEY 'k' ('k')
) ENGINE=InnoDB
DEFAULT CHARSET=gbk
ROW_FORMAT=COMPRESSED
KEY_BLOCK_SIZE=8
下面针对8 KB和16 KB的数据页进行了一次压力测试,虚拟机内存1 GB,Buffer_Pool为600 MB。
/usr/local/bin/Sysbench --test=oltp --MySQL-table-engine=innodb --oltp-table-size=10000000 --max-requests=1000 --num-threads=10 --MySQL-host=192.168.110.140 --MySQL-port=3306 --MySQL-user=admin --MySQL-password=123456 --MySQL-db=test --oltp-table-name=uncompressed --MySQL-socket=/tmp/MySQL.sock run
/usr/local/bin/Sysbench --test=oltp --MySQL-table-engine=innodb --oltp-table-size=10000000 --max-requests=1000 --num-threads=10 --MySQL-host=192.168.110.140 --MySQL-port=3306 --MySQL-user=admin --MySQL-password=123456 --MySQL-db=test --oltp-table-name=compressed --MySQL-socket=/tmp/MySQL.sock run
在实验过程中,先创建了一个有100万行记录的表,经过压缩后,8 KB的数据页的表要比未压缩的16 KB的数据页体积小一半,也就是说,存储同样的数据,8 KB数据页存储的体积要小于16 KB数据页。
用Linux命令ll -h查看数据物理文件大小,如图1-9所示。
在图1-10~图1-12中,16:35~16:50为未压缩时的性能情况,16:55~17:05为压缩时的性能情况。
从图1-10~图1-12可以看出,被请求的数据页小于InnoDB_Buffer_Pool缓冲池大小时,未压缩的性能要稍好于压缩过的,因为压缩会带来额外的CPU消耗,但总体上差异不大。
下面把Sysbench参数调大,再进行一次压力测试:
/usr/local/bin/Sysbench --test=oltp --MySQL-table-engine=innodb --oltp-table-size=10000000 --max-requests=10000 --num-threads=10 --MySQL-host=192.168.110.140 --MySQL-port=3306 --MySQL-user=admin --MySQL-password=123456 --MySQL-db=test --oltp-table-name=uncompressed --MySQL-socket=/tmp/MySQL.sock run
/usr/local/bin/Sysbench --test=oltp --MySQL-table-engine=innodb --oltp-table-size=10000000 --max-requests=10000 --num-threads=10 --MySQL-host=192.168.110.140 --MySQL-port=3306 --MySQL-user=admin --MySQL-password=123456 --MySQL-db=test --oltp-table-name=compressed --MySQL-socket=/tmp/MySQL.sock run
在图1-13~图1-15中,17:25~17:40为未压缩的16 KB数据页的性能情况,17:45~ 17:55为压缩后的8 KB数据页的性能情况。
从图1-13~图1-15中可以看出,被请求的数据页大于InnoDB_Buffer_Pool缓冲池大小时,压缩的性能要好于压缩过的,吞吐量也提高了,最为明显的是CPU Wait/IO降低很多。
从测试的情况来看,你可根据自身的业务情况,选择是否开启数据页压缩功能。
另附上大批量插入时的测试数据,以下是16 KB数据页的情况:
MySQL>showcreatetabletest1\G;
***************************1.row***************************
Table:test1
CreateTable:CREATETABLE'test1'(
'id'int(11)NOTNULL,
'tid'int(11)DEFAULTNULL,
PRIMARYKEY('id')
)ENGINE=InnoDBDEFAULTCHARSET=latin1
1rowinset(0.04sec)
以下是8 KB数据页的情况:
MySQL>showcreatetabletest3\G;
***************************1.row***************************
Table:test3
CreateTable:CREATETABLE'test3'(
'id'int(11)NOTNULL,
'tid'int(11)DEFAULTNULL,
'name'varchar(10)DEFAULTNULL,
PRIMARYKEY('id')
)ENGINE=InnoDBDEFAULTCHARSET=gbkROW_FORMAT=COMPRESSEDKEY_BLOCK_SIZE=8
1rowinset(0.79sec)
图1-16中给出的是先对8 KB数据页test3表进行批量插入,然后对16 KB数据页test1表进行批量插入操作后的情况。
从上面的测试可以看到,在大批量插入的时候,经过压缩的test3表几乎慢了一半,8 KB数据页的性能要比16 KB数据页的性能有所下降。
关于InnoDB表压缩,请参见MySQL5.5手册: