大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!
一张日志表膨胀到2TB,磁盘告警天天响。看到InnoDB支持页压缩,COMPRESSION='zlib'一加,表空间直接省了60%。
省了1.2TB磁盘,看起来是纯赚。但上线一周之后,监控面板上的两个指标开始不对劲:CPU的sys时间占比上升了15个百分点,写入TPS掉了25%。
磁盘省了,CPU和写入吞吐搭进去了。压缩不是免费的午餐,它的代价藏在一个大多数人不会主动去查的地方:解压发生在哪里、什么时候发生、发生多少次。
一、InnoDB页压缩的物理机制
InnoDB页压缩的作用对象是数据页,默认16KB。开启COMPRESSION='zlib'后,页在写入磁盘时被压缩,读取时被解压。
关键设计在于压缩页在Buffer Pool中的存储方式。InnoDB的压缩页使用“压缩页+解压页”的双层结构:磁盘上存压缩后的页,Buffer Pool中可能同时保留压缩页和未压缩页。当查询需要访问某个压缩页时,如果Buffer Pool中没有对应的解压页,需要先解压才能读取数据。
这个设计带来一个容易被忽略的后果:压缩比越高,Buffer Pool中能缓存的“逻辑页”越多,但每次访问未缓存的页时,解压的CPU开销是逃不掉的。
二、隐性代价一:解压消耗CPU,高并发读场景可能成为新瓶颈
压缩算法(zlib)的解压是CPU密集型操作。单次解压16KB页的耗时在微秒级,看起来不大。但高并发读场景下,每秒可能有数万次页访问,解压操作的累积CPU开销会直接体现在sys时间上。
一个可量化的场景:一张读密集型表开启zlib压缩后,QPS下降了约10%,CPU利用率上升了12个百分点。下降的QPS不是磁盘I/O瓶颈导致的,是CPU在解压上花的时间挤占了查询执行的时间。
判断逻辑:如果当前系统的CPU利用率已经在60%以上,且读请求密集,开启页压缩可能让CPU先于磁盘成为新瓶颈。
三、隐性代价二:Buffer Pool的“有效容量”不等于“逻辑容量”
压缩页占用的物理空间更小,同样大小的Buffer Pool能缓存更多的逻辑页。理论上,缓存命中率应该上升。
但实际情况更复杂。压缩页的解压是按需发生的:命中压缩页后,需要解压才能返回数据。如果解压后的页没有被保留在Buffer Pool中(取决于innodb_buffer_pool_size和页的访问频率),下一次访问同一个逻辑页时,可能再次解压。
解压次数不等于页访问次数,解压次数取决于“压缩页命中但解压页未命中”的频率。 在访问模式分散、页复用率低的场景下,这个频率可能很高,解压的CPU开销被反复支付,而缓存带来的收益被稀释。
四、隐性代价三:写入放大与压缩/解压的往返开销
写密集场景下,压缩的代价更直接。
每次写入或更新一个页,InnoDB需要对页执行压缩再落盘。压缩本身消耗CPU;如果页被频繁修改,压缩/解压的往返次数随写入频率线性增长。更关键的是,压缩页的修改可能触发页的重组——压缩后的页在Buffer Pool中被修改后,需要重新压缩才能写回磁盘,这个过程可能伴随额外的I/O。
一个写密集的日志表开启zlib压缩后,写入TPS下降约25%,磁盘写入量反而略有上升(压缩页的重组和碎片导致)。磁盘的节省被写入路径的CPU开销和I/O放大抵消了一部分。
五、什么场景该用,什么场景该绕开
适合开启页压缩的场景:
读多写少,磁盘是明确瓶颈(I/O利用率长期高于80%)
CPU利用率有充足余量(低于50%)
数据本身压缩率高(文本、日志、JSON等冗余度大的数据)
应该绕开的场景:
写密集的交易表(压缩/解压往返频繁)
CPU已经接近瓶颈的系统(解压会加剧CPU争用)
访问模式分散、页复用率低的表(解压开销无法被缓存收益摊薄)
折中方案:如果只是磁盘告警,可以先考虑表分区+冷数据归档,把不常访问的历史数据迁到独立表空间或对象存储,而不是对全表开启压缩。压缩是一个全局生效的选项,分区归档是更精细的粒度控制。
六、小结
InnoDB页压缩省的是磁盘,付出的是CPU和写入吞吐。它的代价不在“压缩算法不好”,而在解压发生的频率和位置——高并发读场景下解压挤占CPU,写密集场景下压缩/解压往返放大写入开销,访问模式分散时缓存收益被解压成本稀释。开启之前先看三个指标:CPU余量够不够、读写比是多少、数据本身的压缩率高不高。 磁盘告警的第一反应不应该是“开压缩”,而是“哪些数据真的需要留在热存储里”。
小耶在手,SQL 不愁
还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~