Cassandra的数据写入遵循LSM-Tree模型,所有写入先落Memtable,刷盘后生成SSTable,再依靠compaction机制把多个SSTable合并整理。可以说,compaction策略直接决定了这个集群的读写性能曲线和磁盘占用水位。策略选错或者参数没调好,常见的结果就是:磁盘空间莫名其妙涨了一倍,读延迟P99时不时飙高,或者压缩线程把CPU和IO全部吃光。这篇文章结合实际运维经验,聊聊如何针对不同负载调整Cassandra的压缩策略。

两大主流策略的原理与取舍
Cassandra内置了多种compaction策略,最核心的是SizeTieredCompactionStrategy(简称STCS)和LeveledCompactionStrategy(简称LCS)。理解它们的工作方式,是做任何调优的前提。
STCS的思路很简单:把大小相近的SSTable分到一组,当同组数量达到阈值(默认min_threshold为4,max_threshold为32)时触发合并。它实现简单、写入放大低,适合写入量大的场景。但缺点也很明显:合并是全量的,新旧数据混合在一起,空间放大严重,极端情况下磁盘占用可以达到真实数据量的两倍以上。而且SSTable数量不可控,读取时可能需要扫描很多文件,读放大随时间推移不断恶化。
LCS则完全不同。它把SSTable组织成多层,L0层接收从Memtable刷下来的新文件,每往下一层,数据量大约是上一层的10倍(由fanout参数控制)。每一层的SSTable之间key范围互不重叠,读取时最多每层查一个文件,读放大稳定且很小。代价是写入放大明显更高,因为一个L1的SSTable被合并时,可能需要把L2中所有与其key范围重叠的文件一起重写。经验数据是LCS的写放大大约是STCS的3到10倍,对SSD比较友好,对HDD机械盘则要谨慎评估。
简单总结:写多读少、允许空间放大的场景用STCS;读延迟敏感、要求磁盘占用可预测的场景用LCS。另外还有一个TimeWindowCompactionStrategy(TWCS),专门为时序数据设计,后面单独说。
如何修改策略与关键参数
修改压缩策略不需要重启节点,直接修改表的属性即可,线上操作时建议一个数据中心一个数据中心地滚动执行,避免全集群同时触发大量compaction任务。
-- 查看当前策略
DESCRIBE TABLE metrics.hourly_data;
-- 将表切换为 LeveledCompactionStrategy
ALTER TABLE metrics.hourly_data WITH
compaction = {
'class': 'LeveledCompactionStrategy',
'sstable_size_in_mb': '160'
};
-- 切换为 TimeWindowCompactionStrategy
ALTER TABLE metrics.sensor_readings WITH
compaction = {
'class': 'TimeWindowCompactionStrategy',
'compaction_window_size': '1',
'compaction_window_unit': 'HOURS',
'expired_sstable_frequency_seconds': '86400'
};
切换策略后,Cassandra并不会立即重写所有现有SSTable,而是随着新数据写入和后台合并逐步过渡。所以切换后的一段时间内,磁盘占用反而可能上升,因为新旧两种布局的文件并存,这一点务必提前和业务方沟通,预留足够磁盘余量,一般建议至少留出50%的空闲空间。
几个值得关注的参数:sstable_size_in_mb控制LCS每层的目标文件大小,默认160MB,增大可以减少文件数和compaction频率,但会提高单次合并的IO压力;min_threshold和max_threshold控制STCS触发合并的SSTable数量门槛;tombstone_threshold(配合tombstone_compaction_interval)决定了删除标记占比达到多少时触发一次清理合并,默认0.2,如果表有大量删除或TTL过期,适当调低到0.1能更快回收空间。
tombstone清除与读放大治理
调优压缩策略绕不开tombstone问题。Cassandra的删除和TTL过期都会产生tombstone记录,读取时必须跨SSTable检查这些墓碑,如果清理不及时,读放大和读延迟会持续恶化,甚至触发toomany-tombstones告警导致读取失败。
第一个手段是让表级别TTL承担过期职责,尽量少用应用层显式DELETE。TTL数据由TWCS或LCS在compaction时自然清除,而显式删除产生的墓碑必须等到gc_grace_seconds(默认86400秒,即10天)之后才能被丢弃。如果业务可以接受,把gc_grace_seconds调小到一天,配合TWCS的窗口机制,能让过期数据以小时级别被物理清除。
第二个手段是监控并及时处理高墓碑率的SSTable。可以通过nodetool compactionstats观察压缩队列,用sstablemetadata工具查看单个文件中tombstone的比例。对于墓碑占比超过30%的表,手动执行一次major compaction往往能立刻降低读放大:
# 查看压缩任务执行情况 nodetool compactionstats -h 127.0.0.1 # 对单表手动触发 major compaction(会带来较大IO压力,避开业务高峰) nodetool compact mykeyspace mytable # 压缩限流,限制压缩使用的吞吐量,单位MB/s nodetool setcompactionthroughput 64
第三个手段是压缩限流。compaction默认可以使用50MB/s的IO吞吐,当磁盘IO成为瓶颈时,如果放任压缩全速运行,会导致读写请求排队;反过来如果限得太死,压缩积压又会推高磁盘水位。建议根据磁盘类型设置:SSD上可以放到128MB/s甚至不限制,HDD上保守地保持32到64MB/s,并通过nodetool compactionstats中的pending任务数持续观察调整。
不同负载下的选型建议
结合实际运维经验,给出几类典型负载的推荐配置。
写多读少的日志类、事件流水类表,首选STCS。这类表数据写入后极少回读,读放大不敏感,STCS的低写放大能保护磁盘寿命和写入吞吐。需要注意的是,如果这类表设置了TTL且数据量大,STCS回收空间很慢,应该改用TWCS。例如数据按天过期,就把窗口设为24小时,每个窗口的SSTable在整体过期后可以被直接整体删除,几乎是零写放大的空间回收方式。
读延迟敏感的用户画像、账户、元数据类表,首选LCS。这类表要求P99读延迟稳定,LCS每层最多查一个文件的特性能把读延迟控制在很小的波动范围内。同时LCS的空间放大约为1.1倍,磁盘容量规划可以做得非常精确。前提是磁盘必须是SSD,且写入量在集群可承受范围内。
混合负载如果拿不准,一个务实的做法是先用LCS观察一周的compaction指标:重点看流量监控中的压缩读写总量与业务写入量的比值。如果写放大超过20倍,说明这块盘扛不住LCS,再退回STCS或考虑TWCS。调优没有银弹,核心思路永远是:先明确业务对读延迟、写吞吐、磁盘占用三者的优先级排序,再让压缩策略为最敏感的那一项服务。
Cassandra压缩策略LeveledCompactionStrategySizeTieredCompactionStrategy修改时间:2026-09-05 23:52:50