3、缓存索引基础:什么是缓存索引?索引在Redis中的存储模型与设计原则。
3.1 缓存索引的本质定义
在Redis缓存场景中,缓存索引并非传统数据库的B+Tree索引,而是一种键值映射结构——通过精心设计的Key模式,将业务查询维度快速定位到对应的Value存储位置。其核心价值在于:用O(1)的哈希查找替代全量扫描。
缓存索引 ≠ 数据库索引
数据库索引:维护数据物理顺序,支持范围扫描、回表查询。
缓存索引:基于Key的精确匹配,本质是哈希表查找,不支持范围扫描(除非使用Sorted Set)。
数据库索引:维护数据物理顺序,支持范围扫描、回表查询。
缓存索引:基于Key的精确匹配,本质是哈希表查找,不支持范围扫描(除非使用Sorted Set)。
3.2 Redis索引的存储模型
Redis底层使用全局哈希表(dict)存储所有键值对。每个Key通过MurmurHash2算法计算哈希值,映射到哈希桶数组的某个槽位。当发生哈希冲突时,采用链地址法解决。
3.3 缓存索引的三种核心设计模式
| 设计模式 | Key结构示例 | 适用场景 | 时间复杂度 |
|---|---|---|---|
| 直接键值索引 | user:{id}:profile |
主键查询、单条记录缓存 | O(1) |
| 复合字段索引 | user:email:{email} |
唯一索引字段查询(如邮箱、手机号) | O(1) |
| 辅助集合索引 | user:city:{cityId}:ids (Set) |
非唯一字段、多对多关系、标签查询 | O(N) 遍历集合 |
设计原则:直接键值索引和复合字段索引应作为首选,辅助集合索引仅在需要按非唯一字段批量查询时使用,且需控制集合大小(建议不超过 5000 个元素)。
3.4 索引设计五大原则
- 唯一性优先:能用唯一字段(ID、邮箱)构建Key,绝不使用非唯一字段。
- 前缀命名空间:使用
业务域:实体:字段:值的层级结构,避免Key冲突。 - 长度控制:Key总长度建议不超过 128 字节,过长会浪费内存并降低哈希计算效率。
- 避免热点桶:使用随机后缀或哈希后缀分散Key,防止大量Key映射到同一哈希桶。
- TTL与淘汰策略:所有缓存索引必须设置过期时间,配合LRU/LFU淘汰策略。
3.5 索引设计反模式与避坑
| 反模式 | 错误示例 | 后果 | 正确做法 |
|---|---|---|---|
| Key过长 | user_profile_info_by_user_id_1001 |
内存浪费、哈希计算慢 | user:1001:profile |
| 无命名空间 | 1001、abc123 |
Key冲突、难以管理 | user:1001、session:abc123 |
| 大集合无分片 | user:all:ids (Set存100万ID) |
单Key内存过大、读写阻塞 | 按哈希分片 user:ids:0 ~ user:ids:15 |
| 忽略过期时间 | cache:data:1 无TTL |
内存持续增长、无法淘汰 | 设置 EXPIRE cache:data:1 3600 |
3.6 复合索引与辅助索引的协同模型
在实际业务中,单一索引往往无法满足所有查询维度。下图展示了主索引 + 辅助索引的协同工作模式:
3.7 索引内存开销估算
每个Key在Redis中都有固定内存开销,设计索引时必须量化评估:
| 组件 | 内存开销 | 说明 |
|---|---|---|
| dictEntry | 32 字节 | 每个Key/Value对的基础结构 |
| Key字符串 | Key长度 + 8 字节 | Redis SDS 结构额外开销 |
| Value指针 | 8 字节 | 指向实际数据对象的引用 |
| 哈希桶数组 | 8 字节 × 桶数量 | 初始 1024 个桶,动态扩容 |
实战警示:一个长度为 32 字节的Key,实际占用内存约为 32 + 8 + 32 + 8 = 80 字节。1000 万个索引Key仅基础开销就达 800 MB,还不包含Value本身。设计索引时必须压缩Key长度,优先使用数字ID而非字符串。
3.8 索引设计决策矩阵
| 业务查询模式 | 推荐索引类型 | 数据结构 | 示例 |
|---|---|---|---|
| 通过主键查询单条记录 | 直接键值索引 | String / Hash | user:1001 → Hash |
| 通过唯一业务键查询 | 复合字段索引 | String | user:email:alice@example.com → user:1001 |
| 按分类/标签查询ID列表 | 辅助集合索引 | Set | article:tag:redis:ids → Set of article IDs |
| 按时间范围查询 | 有序集合索引 | Sorted Set | order:create:202401 → score=timestamp |
| 多字段组合查询 | 复合Key + 辅助索引 | String + Set | order:status:paid:city:beijing:ids |
核心结论:缓存索引设计的本质是用空间换时间——通过冗余存储查询维度到Key中,换取O(1)的查询性能。但必须严格控制索引数量,每个额外的索引都意味着写入时的双倍开销和内存的线性增长。建议每个实体不超过 3 个辅助索引。