本节摘要:默认策略只给了粗档位,生产负载的账目往往需要更细的调节阀。本节盘点五个进阶特性:动态层级让深层容量跟着数据量自动伸缩,子压缩把大任务切片并行,手动压缩把整理时机攥回自己手里,压缩过滤器让清理逻辑带上业务语义,值分离让大对象不再拖累主树。每个特性讲清它解决哪本账的哪个具体痛点,以及开启的代价。
先说清楚这些特性的共同出身:它们都不是「更快」的开关,而是「更准」的开关。默认参数假设负载是均衡的——数据量稳定、键分布均匀、值大小规整。真实业务很少这么配合:有的库白天冷清夜里洪峰,有的键空间九成集中在头部,有的大对象混在小键里把缓存搅得一团糟。本节五个特性,各治一种失衡。
层级式有个默认假设:每一层的容量目标按十倍递增,最深层管着全部存量。问题在于数据量是长出来的——一个新库只有几百兆,却已经背着「最深层目标几个 TB」的层级结构,数据被迫挤在前两层反复归并,写放大虚高;等数据真涨上来,层级结构才物尽其用。
动态层级改的就是这件事:打开这个开关后,引擎按实际数据总量倒推各层目标——最深层固定吃下全部存量,往上逐层除以倍增系数。数据小时层级浅而薄,写放大低;数据长大后层级自动加深,秩序照旧。它是层级式在「数据量剧烈变化」场景下的第一颗推荐旋钮,尤其适合数据从零起步持续增长的业务库。代价几乎为零,主要风险是老版本引擎上该特性与某些手动压缩路径的兼容细节,开启后照例压测验证。
深层全量归并是压缩系统里最大的单体任务:输入几个 GB,单线程搬十几秒,期间前台若来一个写入洪峰,三道闸门立刻敏感。子压缩的思路是把一次任务的键空间切成若干连续区间,每个区间独立走「读输入、归并、写输出」的全流程,多线程同时开工。
它解决的不是总搬运量——切片不减少字节——而是单任务的时长与时延尖刺:同样搬十个 GB,八线程切八片三秒搬完,好过单线程三十秒里前台一直心惊肉跳。开启的账目代价是多份中间状态与调度开销,切片过细反而费 CPU。经验是给并行度留一档余量,配合压缩线程池配额一起调,用「单次压缩最长耗时」这个指标验证效果。第 4 章讲线程模型时提过这个参数,这里补全了它在账目上的意义。
自动压缩调度再聪明,也读不懂业务的节奏。典型场景:批量导入一个亿的历史数据,逐条写入触发一路归并,搬运量是数据本身的十几倍;而导入完成后数据不再更新,理论上只需要一次整体整理。
手动压缩把整理时机攥回业务手里。常见配方是导入期主动降噪——临时关掉自动压缩或把触发阈值拉高,让数据以最少的搬运路径快速落盘;导入完成后执行一次手动全区间压缩,把冗余版本一次性清干净,再把自动档恢复原样。总搬运账可以从十几倍压到三倍上下。两个纪律必须守住:其一,手动压缩是同步重 IO 操作,放在业务低峰执行并限时,别让它撞上线上洪峰;其二,导入期关闭自动压缩的时间窗要设闹钟——忘了恢复,零层会无限堆积,读路径慢慢瘫痪,这类事故在工单里出镜率极高。
标准归并只认通用规则:被覆盖的旧值、沉底后的墓碑、过期的存活期。可有些账它算不了——缓存型业务想淘汰「很久没人访问」的键,风控业务想让某些记录「只活三十天」,这些语义写在业务逻辑里,引擎看不见。
压缩过滤器是个回调钩子:归并逐条裁决时,把每个键值对递给业务代码过目,业务说扔就扔,说留就留。清理成本几乎为零——数据反正要被读出来归并,顺手过滤不增加额外读取。这是「写时处理」的典型入口,相当于把一轮全量数据清洗免费搭在压缩的车上。纪律同样明确:过滤器必须快、必须无阻塞、必须幂等——它运行在压缩关键路径上,一个慢回调能把整个引擎的后台拖垮;它一旦丢弃数据就不可恢复,过滤逻辑里的每个判断都该按「删库代码」的标准评审。
用户画像、特征向量、文档快照这类负载有个共同痛点:值动辄几十 KB 起步。让它们挤在主树里,三个账本同时恶化——数据块被大值撑大,点查缓存命中率暴跌;归并搬运大值,写放大的绝对字节数失控;压缩编码对二进制大对象无能为力,空间账也难看。
值分离把账拆成两本:主树里只放键和一个小小的引用,真实的大值另存进专用的追加写文件;归并时主树只搬引用不搬值,体积骤减;专用文件由独立的回收流程按引用计数清退失效值。效果是大对象的写入路径几乎不再触发全量搬运,写放大的账从「按值计价」变成「按索引计价」。代价是读大值多一次寻址,以及引用与实体两本账的一致性管理——小值负载开了它反而多付一层间接。判断标准很简单:值的中位数尺寸超过块缓存的意义边界(几 KB 往上),就该评估分离。
这些特性落进配置代码,长这个样子:
// 动态层级:数据量增长型库的第一颗旋钮 options.level_compaction_dynamic_level_bytes = true; // 子压缩:单任务切片并行,配合后台线程配额 options.max_subcompactions = 4; // 存活期与周期整理:给有时效的数据兜底 options.ttl = 30 * 24 * 3600; // 三十天未沉降的数据强制整理 options.periodic_compaction_seconds = 7 * 24 * 3600; // 每周巡检一轮 // 批量导入配方:导入期关自动压缩,导完手动整理后恢复 // 导入前:options.disable_auto_compactions = true // 导入后:db->CompactRange(全区间) 然后恢复自动档,并设好恢复闹钟
进阶特性单独用都有明确的账,组合使用时另有两条注脚值得记。其一,子压缩与手动压缩可以叠加:批量导入后的那次全区间整理,配上切片并行,能把「整理窗口」压到业务可接受的最短——导入后整理从以小时计降到以十分钟计,是两条特性相乘的典型收益。其二,动态层级与手动压缩要错开用:手动整理期间数据量剧烈变化,动态层级会同步调整各层目标,两股力量叠加会让整理后的层级分布偏离预期;正确姿势是先整理、后恢复自动档,让动态层级在稳态里重新找平。