设计一张百亿级日志表时,分区键、排序键和主键你会怎么选?
我会先定目标和容量,再拆核心链路:PARTITION BY 决定数据片段的分区目录和生命周期管理;ORDER BY 决定片段内的物理排序,是查询裁剪和压缩的核心;PRIMARY KEY 定义稀疏主索引,默认通常与排序键相同或是其前缀,但不保证唯一。
我不会直接画架构图,会先确认目标、规模和一致性要求。区分数据片段管理、物理排序和稀疏索引三类建模能力。PARTITION BY 决定数据片段的分区目录和生命周期管理;ORDER BY 决定片段内的物理排序,是查询裁剪和压缩的核心;PRIMARY KEY 定义稀疏主索引,默认通常与排序键相同或是其前缀,但不保证唯一。 大多数场景先把高频过滤、基数与数据局部性用于设计排序键,再按删除、归档和分区裁剪需求选择粒度适中的分区键。
容量有了以后,再把入口、核心处理和数据落点串起来。沿着「按 PARTITION BY 路由数据 → 按 ORDER BY 建立物理顺序 → PRIMARY KEY 生成稀疏标记 → 查询逐级裁剪 → TTL/删除按分区管理」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 按 PARTITION BY 路由数据 分区用于数据生命周期和粗粒度裁剪,通常按月/日等低基数时间窗口设计。 system.parts:分区与 Part 物理分布。 EXPLAIN indexes=1:分区/主键裁剪。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
关键参数要从峰值流量和资源上限反推。以三种 ORDER BY 回放 Top 查询,比较读取 granule 和压缩比。
正常链路之外,还要设计失败补偿和可验证的恢复流程。设计者希望查询单用户更快,把高基数 userid 作为分区,结果每天产生海量分区与小 Part。改为按月分区、ORDER BY (tenantid,userid,eventtime) 后,生命周期管理与用户过滤都能兼顾。 高基数业务字段做分区:分区数量:低基数时间分区。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「readrows/resultrows」为主基线,记录值应满足「记录稳态基线」;同时保存 分区数量、每分区 active parts,使后续变化能够回到同一时间轴比较。
最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 按月分区:长期留存与常规时间查询:分区数量稳定、管理方便:删除粒度为月。 按日分区:日级删除或单日数据巨大:裁剪与运维粒度细:分区数量和小 Part 更多。 不分区/单分区:小表或无需周期删除:结构最简单:长期大表维护和删除成本高。 选型至少带上 日增量、分区规模、查询并发、扫描行数和压缩比,并用上面的量化基线验证;未知数据应明确为待测假设。