
位图索引不是“建个 bitmap 就完事”
直接用 std::vector<bool></bool> 或 std::bitset 存 0/1,查得慢、内存不紧凑、并发一压就卡——这不是位图索引,只是位容器。真要提速,得让位操作对齐 CPU 字长、跳过全 0/1 段、支持快速 rank/select,并和查询谓词(比如 WHERE status = 'active')的列值分布强绑定。
rank/select 必须手写,std::bitset::count() 不够用
数据库里查“第 1000 个 active 记录在哪”,本质是 select(1000);查“位置 5000 前有多少个 active”,本质是 rank(5000)。标准库没提供 select,std::bitset::count() 只能从头扫,O(n);而生产级位图索引必须做到 O(log n) 或常数时间。
- 用分层索引:每 64 位存一个前缀和(popcount),再配合
__builtin_popcountll()算局部 - select 操作先二分定位到 block,再用
_pdep_u64()(BMI2)或查表法找第 k 个置位 - 别在运行时动态 resize——block 大小固定为 64/256/1024 位,内存按页对齐,方便 mmap 和 SIMD 扫描
AND/OR 操作不能逐位 & |,得用 word-level 并行
两个百万级位图做 AND,如果循环调用 operator&,哪怕用 std::bitset,也是 100 万次分支+内存访问;实际要按机器字(64 位)批量处理,且跳过全 0 字——这是性能差距的主因。
- 遍历用
uint64_t*指针,每次取一个字,word & other_word一次得 64 位结果 - 加
if (word == 0 || word == ~0ULL)分支预测友好地跳过全零/全一区段 - AVX2 可用
_mm256_and_si256()一次处理 256 位,但要注意对齐和 fallback 路径 - OR 同理,但注意:位图 OR 结果可能比原图更稀疏,后续 rank/select 成本会上升
列值编码方式决定位图数量和 cache 命中率
一个 status 列有 5 个取值,建 5 张位图?错。高频值(如 'active')单独一张,低频值合并成 'other' 一张,再加一张 is_null 图——总张数少,L1 cache 更容易装下热数据。
立即学习“C++免费学习笔记(深入)”;
- 避免“每个 distinct 值一张图”,尤其对高基数列(如 user_id),改用 Roaring Bitmap 这类压缩结构
- 位图顺序要和主表物理顺序一致(row-id 对齐),否则随机访存毁掉所有优化
- 更新场景下,
std::atomic<uint64_t></uint64_t>保护单个 word 是可行的,但别锁整张图——用 CAS + retry 或分段锁
位图索引快的前提,是它根本不参与“通用计算”,只服务特定模式的等值/IN/IS NULL 查询;一旦出现范围查询或函数表达式(如 UPPER(name)),这套机制就自动失效——这点常被忽略,但恰恰是上线后查询变慢的根源。










