磁盘空间告警几乎是每个DBA都会遇到的问题,尤其是MySQL的系统表空间文件ibdata1,随着业务运行不断膨胀,动辄几十GB甚至上百GB。很多同学第一反应是删数据或者扩容,但其实从MySQL 5.7开始,InnoDB提供了一项透明页压缩(Transparent Page Compression)功能,通过innodb_page_compressed相关参数就能显著压缩表空间占用的磁盘空间。本文就来详细说说这个功能怎么用、开启前要确认什么、以及它的适用场景。

一、透明页压缩的工作原理
InnoDB透明页压缩的核心思路是:在将16KB的页写入磁盘之前,先对其进行压缩(默认使用Punch Hole方式结合LZ4或ZLIB算法),压缩后产生空洞,再借助文件系统对稀疏文件(Sparse File)的支持,通过punch hole操作把空洞部分真正归还给操作系统。这样磁盘上实际占用的空间就只有有效数据部分的大小。
这套机制之所以被称为“透明”,是因为它对应用层完全无感。SQL的读写、索引结构、页分裂逻辑都没有任何变化,压缩和解压完全发生在InnoDB的IO层。读取数据时,InnoDB会先将磁盘上的压缩页读入内存并解压还原成完整的16KB页,再走后续的处理流程。
需要注意的是,这项功能强依赖文件系统支持punch hole,目前XFS和ext4都支持(ext4需要内核较新版本),如果你用的是不支持稀疏文件的文件系统,参数会设置失败。这也是很多人开启失败后不知道原因的关键点。
二、开启前的准备与版本条件
首先确认MySQL版本。透明页压缩从MySQL 5.7.7开始引入,建议使用5.7后期版本或8.0,成熟度更高。可以通过下面的命令查看当前版本:
SELECT VERSION(); -- 输出示例:8.0.32
其次确认文件系统。透明页压缩要求文件系统支持punch hole打洞操作,推荐使用XFS。可以用下面的命令确认:
# 查看数据目录所在分区的文件系统类型 df -T /var/lib/mysql # 查看内核是否支持hole punching cat /proc/filesystems | grep -i xfs
如果文件系统是ext4,需要确保挂载参数没有禁用相关特性;如果是NFS或者某些云厂商的定制文件系统,则很可能不支持,开启时会直接报错或者压缩不生效。这一点在生产环境上线前务必在测试环境验证清楚,避免上线后才发现空间没有减少。
最后还要确认一个前提:透明页压缩只对独立表空间生效,也就是要求innodb_file_per_table参数开启(默认就是开启的)。系统表空间ibdata1本身是不支持压缩的,这一点经常被误解,后面会详细说明。
三、具体开启步骤与参数设置
先说清楚一个容易混淆的点:严格来说,innodb_page_compressed是对新表生效的表级选项,需要建表时或者通过ALTER TABLE来指定。对已有表开启压缩的方式如下:
-- 对已存在的表开启页压缩
ALTER TABLE orders COMPRESSION='zlib';
-- 新建表时直接指定
CREATE TABLE orders_detail (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_id BIGINT UNSIGNED NOT NULL,
detail TEXT,
PRIMARY KEY (id),
KEY idx_order_id (order_id)
) ENGINE=InnoDB COMPRESSION='zlib';执行ALTER后,压缩并不会立即对所有页生效,已有的数据页会在后续被修改并重新写盘时才真正压缩,效果是渐进式的。如果想立即让整张表压缩生效,需要重建表:
-- 优化表以立即应用压缩(内部会重建) OPTIMIZE TABLE orders; -- 或者 ALTER TABLE orders ENGINE=InnoDB;
关于压缩算法的选择,MySQL目前支持zlib和lz4。zlib压缩率更高,节省空间更多,但CPU开销略大;lz4压缩和解压速度快,压缩率略低。对于读多写少、追求空间节省的场景选zlib,对于写入频繁、对延迟敏感的场景选lz4。另外还有一个参数innodb_page_compression_level可以控制压缩级别,取值1到9,数值越大压缩率越高、CPU消耗越大,默认值9,一般保持默认即可。
验证压缩是否生效,可以对比文件的实际磁盘占用和逻辑大小:
# ls -s的第一列是实际磁盘块占用,第二列是逻辑大小 ls -ls /var/lib/mysql/test/orders.ibd # 如果实际占用明显小于逻辑大小,说明压缩已生效 du -h --apparent-size /var/lib/mysql/test/orders.ibd du -h /var/lib/mysql/test/orders.ibd
四、性能影响与常见问题排查
压缩不是免费的午餐。写入路径上多了一次压缩计算,读取路径上多了解压开销,同时页内空洞可能导致单次IO的有效数据量减少,极端情况下随机读性能会有一定下降。经验上看,使用lz4时性能损耗通常在5%以内,zlib则可能在10%到20%之间,具体和数据的可压缩性、IO模式强相关。建议上线前用真实业务流量压测对比。
有几个常见问题需要留意。第一,前面提到的系统表空间ibdata1无法压缩,如果ibdata1膨胀严重,正确做法是把数据导出后重建实例,并保持innodb_file_per_table开启,让数据都落在独立的ibd文件中,然后再对各表开启压缩。第二,如果设置COMPRESSION后磁盘占用没有变化,优先检查文件系统是否支持punch hole,其次检查是否有足够的新页写入触发压缩。第三,主从复制的场景下,从库的表结构里压缩属性可以和主库不同,压缩不影响物理复制的数据一致性,但为了运维一致性,建议主从保持相同设置。
最后补充一点备份相关的注意事项:使用XtraBackup等物理备份工具时,稀疏文件的空洞处理方式可能与普通文件不同,部分工具版本会自动处理,部分需要额外参数,备份后一定要验证恢复实例的表空间占用是否符合预期。监控方面,可以定期通过du命令统计数据目录实际占用,与逻辑大小做对比,形成压缩率的长期观测数据,为后续容量规划提供依据。
innodb_page_compressed系统表空间压缩InnoDB压缩修改时间:2026-09-05 17:26:52