什么是低基数什么是高基数
在数据库和存储引擎的语境中基数指的是某个字段中不同唯一值的数量。低基数唯一值很少大量文档共享相同的值。高基数唯一值很多几乎每个文档的值都不同。 直观对比特征低基数高基数唯一值数量几十 ~ 几千几十万 ~ 数十亿重复率极高极低典型例子性别、状态码、省份、星期几用户ID、订单号、时间戳、UUID字典编码效果✅ 极好字典小ID位宽窄❌ 很差字典本身比原始数据还大位图索引效果✅ 每个值一个位图高效❌ 需要海量位图内存爆炸B-Tree 效果⚠️ 区分度低范围扫描退化✅ 天然适合Lucene DocValues⚠️ 可用但浪费✅ 位宽压缩正好适配 量化感知假设你有1000 万条文档gender2 个唯一值 → 基数 2 →极低基数province34 个唯一值 → 基数 34 →低基数age~120 个唯一值 → 基数 120 →低基数city_code~3000 个唯一值 → 基数 3K →中等基数user_id~800 万个唯一值 → 基数 8M →高基数timestamp~1000 万个唯一值 → 基数 ≈ 文档数 →极高基数 为什么基数重要因为不同的压缩/索引方案对基数的敏感度完全不同这也是上一轮讨论各种数值存储方案时反复提到基数的原因字典编码的压缩率 ≈log₂(基数) / 原始位宽。基数越低收益越大当基数接近文档总数时字典 ID 序列反而比原始数据更大。位图索引的空间 ≈基数 × 文档数 / 8。基数翻倍空间翻倍。所以只对低基数字段建位图。Lucene 的位宽自适应numBitsPerValue ceil(log₂(max - min 1))。它不关心有多少唯一值只关心值域跨度。所以即使基数很高只要值域集中比如 1000 万个文档的值都在 0~65535 之间仍然只需 16 bits/值压缩效果很好。B-Tree 的查询效率取决于区分度。低基数时等值查询返回海量行索引选择性差高基数时近似 O(log N)效率高。一句话记忆基数 唯一值个数。选存储/索引方案前先问自己这个字段有多少种不同的值答案直接决定了哪种方案最合适。Lucene 当前采用的“位宽自适应 分块/全局二选一”方案本质上是一种面向随机访问的轻量级列存压缩。但在更广泛的数据库和存储引擎领域针对数值型数据还有多种截然不同的方案。以下按设计哲学分类梳理1. 字典编码 位图索引代表系统ClickHouse (LowCardinality)、Apache Doris、Elasticsearch Keyword核心思想不直接存数值本身而是将每个唯一值映射为一个整数 ID再对 ID 序列做压缩。适用场景低基数数值状态码、枚举、年龄等与 Lucene 的区别Lucene 的 SortedNumericDocValues 不做字典编码它假设值是连续或近似连续的整数直接用位宽压缩。字典编码在低基数时空间效率远超位宽压缩但高基数时字典本身的开销会成为瓶颈。2. 通用列式压缩算法代表系统Parquet / ORC / Arrow、DuckDB核心思想在 RLE / Delta / Bit-Packing 之上叠加通用块压缩器。典型组合Delta Encoding → RLE → ZSTD/LZ4/Snappy与 Lucene 的区别Lucene 的.dvd不使用任何通用压缩器不用 LZ4/ZSTD因为 DocValues 需要 O(1) 随机访问通用压缩器的解压代价不可接受。Parquet/ORC 是面向批量扫描优化的可以容忍解压开销换取更高压缩率。3. 向量化 SIMD 压缩代表系统FastPFOR、StreamVByte、SIMD-BitPacking核心思想利用 CPU 的 SIMD 指令集一条指令同时处理 128/256/512 bit 的位宽打包/解包。与 Lucene 的区别Lucene 的PackedInts/DirectWriter是纯标量 Java 实现。FastPFOR 等库在解码吞吐上可达 Lucene 的5~10 倍但通常作为底层编解码库嵌入使用而非独立的存储格式。Lucene 10 已开始引入 Panama Vector API 做类似优化。4. B-Tree / LSM-Tree 行存式索引代表系统InnoDB (BTree)、RocksDB / LevelDB (LSM)核心思想数值作为 Key 或 Value 的一部分按排序结构组织通过树遍历定位。与 Lucene 的区别这是完全不同的范式。Lucene DocValues 是纯列存、无树结构、O(1) 定位B-Tree/LSM 是面向范围查询和有序遍历优化的随机点查需要 O(log N) 次 I/O。DocValues 不适合做范围聚合以外的复杂数值查询而 B-Tree 恰好擅长这个。5. 专用时序/浮点压缩代表系统Gorilla (Facebook TSDB)、InfluxDB TSM、QuestDB核心思想整数时间戳Delta-of-Delta 编码二阶差分因为时间间隔通常是常数。浮点值XOR 编码相邻值的 IEEE754 表示通常只有少数 bit 不同。与 Lucene 的区别Lucene 把浮点数转成 sortable long 后当整数处理丢失了浮点数的局部相关性。Gorilla XOR 编码对浮点监控数据的压缩率比 Lucene 方案高一个数量级但完全不适用于非时序数据。 方案选型速查表需求特征推荐方案Lucene DocValues 适合吗全文检索附带数值过滤/排序Lucene SortedNumeric✅ 最佳选择大规模分析查询、高压缩率Parquet/ORC ZSTD❌ 压缩率不够低基数数值精确匹配字典编码 位图⚠️ 可用但非最优高频随机点查 范围查询B-Tree / LSM-Tree❌ 不支持高效范围查时序监控数据Gorilla / Delta-RLE❌ 未利用时序特性极致解码吞吐SIMD Bit-Packing⚠️ 标量实现有差距总结Lucene 的方案是为搜索引擎中的数值辅助字段量身定制的——牺牲了极致压缩率和范围查询能力换取了 O(1) 随机访问、零依赖、与倒排索引无缝配合的特性。如果你的场景脱离了搜索上下文纯分析、纯时序、纯事务上述替代方案通常在各自领域表现更优。