← 中文资源

简体中文版本

设计存储前先画像列基数

度量每列的去重数、空值率与频率偏斜,让数据本身决定枚举、索引与分区设计。

阅读英文原文

目标

不做基数画像就做存储设计,本质是披着架构图的猜测。四 个取值且其中一个占 94% 的列、两百万唯一值的用户列、380 种设备的列,是三种完全不同的设计输入——而且每一个都能用一条查询量出来。让度量先行,索引与约束才有依据。

步骤要点

  1. 用 DuckDB 对每个关键列算去重数、空值数与总数,得到一张列统计表作为设计输入。
  2. 对低基数列继续测频率偏斜:某值占 94% 意味着全量 btree 无选择性,而只针对稀有值的部分索引反而出色。
  3. 按量出的数字导出设计:status 加 CHECK 约束不建全量索引,failed 建部分索引只扫 6% 行,user_id 与时间建复合索引服务范围查询。
  4. 抽样统计与全量对比验证:全量去重数与样本在预期范围内一致,画像可以外推后再落设计。
  5. 把画像 SQL 作为 schema 变更的附件入库,记录采样日期与比例,下次重设计从证据出发。

排错提示

去重计数爆内存就对高基数列用 APPROX_COUNT_DISTINCT 或抽样估计,精确计数只留给驱动枚举决策的低基数列;样本说 4 个状态生产出现 6 个,是样本漏了稀有值,状态全集从约束流程枚举,把基数下界当下界对待;频率偏斜随季节变就在旺季与淡季各画像一次再选部分索引谓词,十月稀有的状态一月可能主导。

适用边界

基数只是一个维度,更新频率与行宽同样影响索引选择,需一并权衡;抽样统计随数据增长漂移,量级里程碑时要安排画像重跑。

代码示例

column    distincts   nulls    top frequency
--------  ---------   -----    ---------------------
status         4        0%     completed  94.1%
user_id    2.1M         0%     (flat)
device       380      1.9%     iOS 41.2%

informed design:
  status  -> CHECK constraint, no btree (4 values: useless)
  failed  -> partial index (6% of rows, real selectivity)
  user+time -> composite (created, user_id) for range queries

vs. the previous design: btree(status) -- full-scans anyway