3. 缓存索引选择策略:基于查询模式、数据分布与写入频率
缓存索引的选择并非“一劳永逸”,而是需要根据业务数据的访问特征进行动态权衡。错误的索引策略会导致缓存命中率骤降、写入放大或内存浪费。本节从三个核心维度——查询模式、数据分布和写入频率——出发,给出可落地的索引选择策略。
3.1 基于查询模式选择索引
查询模式决定了索引的键结构和粒度。常见的查询模式分为三类:
| 查询模式 | 特征 | 推荐索引策略 | 典型场景 |
|---|---|---|---|
| 点查(Point Query) | 通过唯一键获取单条记录 | 直接使用主键或业务唯一键作为缓存 Key | 用户信息查询、订单详情 |
| 范围查询(Range Query) | 按时间、ID 区间或排序字段批量拉取 | 使用有序集合(如 Redis ZSET)或复合前缀索引 | 最近订单列表、分页商品 |
| 多维查询(Multi-Dimensional) | 按多个非主键字段组合过滤 | 构建倒排索引或二级索引缓存(如 Redis Search) | 标签筛选、属性搜索 |
实践建议:对于点查场景,优先使用
user:{id} 这种扁平 Key,避免使用哈希嵌套过深导致序列化开销。对于范围查询,务必在 Key 中嵌入排序字段(如 order:2025:04:{page}),利用 Redis 的字典序实现高效分页。
3.2 基于数据分布选择索引
数据分布直接影响缓存的热点集中度和分片效率。选择索引时需考虑以下分布特征:
- 均匀分布:数据在所有 Key 上均匀散列,适合使用简单哈希分片,每个分片负载均衡。
- 倾斜分布:少数 Key 占据绝大部分访问量(如头部商品、大 V 用户),需要为热点 Key 设计独立缓存策略,避免单分片过载。
- 稀疏分布:Key 空间极大但有效数据少,适合使用布隆过滤器(Bloom Filter)前置过滤,避免无效查询穿透到数据库。
关键指标:通过监控缓存集群的
get_hit_ratio 和 key_access_frequency 分布曲线,可以判断数据是否倾斜。当 20% 的 Key 贡献了 80% 的访问量时,必须引入本地缓存(如 Caffeine)或热点探测机制。
3.3 基于写入频率选择索引
写入频率决定了索引的更新成本和一致性模型。高频写入场景下,索引的维护开销可能超过查询收益。
| 写入频率 | 索引更新代价 | 推荐策略 | 一致性模型 |
|---|---|---|---|
| 低频写入(< 100 QPS) | 低 | 同步更新索引,保证强一致性 | 强一致性 |
| 中频写入(100 ~ 1000 QPS) | 中 | 异步更新索引 + 最终一致性补偿 | 最终一致性 |
| 高频写入(> 1000 QPS) | 高 | 写时只更新主库,缓存索引设置 TTL 过期 | 弱一致性 / 准实时 |
高频写入陷阱:不要对高频写入字段建立二级缓存索引。例如,商品库存字段每秒更新数千次,如果同时建立缓存索引,每次写入都会触发索引重建,导致 CPU 飙升和缓存雪崩。正确做法是:库存只做数据库写入,缓存索引仅用于查询频率远高于写入频率的字段(如商品标题、描述)。
3.4 综合决策矩阵
实际业务中,三个维度往往交织在一起。以下矩阵帮助快速定位索引策略:
| 查询模式 | 数据分布 | 写入频率 | 推荐索引类型 | 示例 |
|---|---|---|---|---|
| 点查 | 均匀 | 低频 | 主键哈希索引 | 用户 ID → 用户信息 |
| 点查 | 倾斜 | 中频 | 本地缓存 + 分布式缓存 | 热门商品详情 |
| 范围查询 | 均匀 | 低频 | 有序集合(ZSET) | 历史订单列表 |
| 范围查询 | 稀疏 | 高频 | 布隆过滤器 + 空值缓存 | 日志 ID 查询 |
| 多维查询 | 均匀 | 低频 | 倒排索引(Redis Search) | 商品标签搜索 |
| 多维查询 | 倾斜 | 高频 | 仅索引热门维度,冷维度走 DB | 实时推荐过滤 |
核心原则:索引选择本质是读/写/存三者的权衡。读多写少用同步索引,写多读少用 TTL 过期,读写均衡用异步补偿。没有银弹,只有基于实际 QPS 和延迟要求的持续调优。
3.5 避坑清单
- 不要对高频写入字段建立二级索引:索引更新成本会超过查询收益,导致系统吞吐下降。
- 不要忽略数据倾斜:均匀分布假设下设计的索引,在倾斜场景中会导致单分片热点崩溃。
- 不要对稀疏分布使用全量哈希:大量空 Key 会浪费内存,布隆过滤器是更好的前置方案。
- 不要混合使用多种一致性模型:同一索引体系内,强一致和最终一致混用会导致数据混乱。