当前位置:首页 > 问答 > 正文

Redis里那些键到底怎么被索引,有效索引机制是咋回事呢?

Redis里的键是怎么被找到的?这事儿其实不复杂,但很巧妙,你可以把Redis的数据库想象成一个巨大的字典或者哈希表(来源:Redis官方文档将数据库描述为键值对字典),这个哈希表里存放了所有的键值对,键就是索引的关键。

核心:全局哈希表

当你在Redis里设置一个键值对,SET username "张三",Redis会做这么几件事:

  1. 它会对键 "username" 进行一个哈希运算,算出一个数字,这个数字代表一个位置,我们称之为哈希桶的索引。
  2. Redis会去那个巨大的全局哈希表里,找到对应编号的“桶”(bucket)。
  3. 然后把键 "username" 和值 "张三" 作为一个条目(entry)放进这个桶里。

当你需要读取 GET username 时,Redis重复同样的过程:对 "username" 做哈希,找到对应的桶,然后在桶里找到具体的键,最后把值 "张三" 返回给你,因为这个操作直接基于哈希值定位,所以速度非常快,时间复杂度是O(1),意思是无论你数据库里存了一万个键还是一亿个键,查找一个特定键的速度在理论上是差不多的(来源:哈希表查找的平均时间复杂度为O(1)这一通用计算机科学原理)。

Redis里那些键到底怎么被索引,有效索引机制是咋回事呢?

哈希冲突与渐进式Rehash

这里有个问题:哈希桶的数量是有限的,但键的数量可以无限增长,如果很多键的哈希值算出来都指向同一个桶,那么这个桶里的条目就会越来越多,形成一个链表,查找的时候,如果这个链表很长,就得一个个往下找,速度就会变慢,这就是所谓的“哈希冲突”。

为了解决这个问题,Redis会在这个全局哈希表变得“拥挤”的时候进行“扩容”(来源:Redis源码中dict.c文件实现的渐进式重哈希机制),它会准备一个更大的新哈希表(比如大小是原来的两倍),然后开始一个叫做“渐进式Rehash”的过程。

Redis里那些键到底怎么被索引,有效索引机制是咋回事呢?

这个过程很聪明,它不是一次性把所有键从旧表搬到新表(那样会卡住Redis,无法处理其他请求),而是分批次、慢慢搬,每当有新的读写请求到来时,Redis除了处理这个请求本身,还会顺便把旧哈希表里一个小部分桶中的键值对迁移到新哈希表,这样,经过多次请求后,所有数据就都慢慢地、平滑地转移到了新表上,迁移完成后,旧表被释放,新表正式上岗,这样一来,哈希桶的数量总是能跟得上键的数量,保持每个桶里的链表尽可能短,保证高效的查找速度。

什么是“有效”的索引机制?

对于Redis来说,“有效”的索引机制主要体现在两个方面:

Redis里那些键到底怎么被索引,有效索引机制是咋回事呢?

  1. 对精确键匹配的极致优化:上面说的全局哈希表,就是为 GETSETDEL 这种直接通过完整键名来操作的命令服务的,这是Redis最核心、最高效的索引方式,只要你用的是精确匹配,速度就非常有保障。

  2. 对特定模式查询的辅助结构:但有时候你需要模糊查找,比如用 KEYS user* 命令找出所有以 "user" 开头的键,全局哈希表本身无法高效支持这种操作,因为它依赖于精确的哈希值,那Redis怎么办呢?它其实并没有给键名本身建立像数据库那样的“B+树”索引。

    • 对于 KEYS 命令,Redis的做法简单直接但也比较“笨”:它会遍历全局哈希表中的所有键,然后和你给的模式(如 user*)进行匹配(来源:Redis官方文档对KEYS命令的说明指出它会遍历所有键),当数据库里键非常多的时候,这个命令会非常慢,而且会阻塞其他请求,所以一般禁止在生产环境使用。
    • 那有没有高效一点的方法呢?有,这就是Redis的另一种索引机制——Redis自己的数据结构充当索引,这其实是Redis实现“高效索引”更常见、更实用的方式,举个例子,如果你想快速查询属于某个用户的所有订单ID,你不会用 KEYS order:user123:*,而是会这样做:
      • 你除了在Redis里存储每个订单的详细信息(如键 order:user123:1001 对应订单详情),还会维护一个集合(Set)或列表(List),这个集合的键可以是 user123:orders,里面专门存放这个用户的所有订单ID(如 1001, 1002)。
      • 这样,当你要查询用户123的所有订单时,你不需要扫描全部键,只需要执行 SMEMBERS user123:orders,就能立刻拿到所有订单ID列表,这个集合(Set)数据结构本身也是由Redis高效实现的,它的查找速度也很快。
      • user123:orders 这个集合,就充当了一个“索引”的角色,很多看似复杂的查询,都是通过程序员在设计数据存储时,巧妙地利用Redis内置的数据结构(如 Set, List, Sorted Set, Hash)来预先构建这种“二级索引”实现的。

总结一下

回到问题“Redis里那些键到底怎么被索引,有效索引机制是咋回事?”,答案是双重的:

  • 底层机制:依靠一个可动态扩容(通过渐进式Rehash)的全局哈希表,来提供对键名精确匹配的、极其快速的索引能力,这是Redis性能的基石。
  • 上层应用:对于模式匹配关系型查询,Redis本身不提供自动的复杂索引,真正的“有效索引”来自于使用者根据业务需求,主动利用Redis的各种数据结构(Set, Sorted Set等)来手动构建辅助的索引结构,这种设计哲学使得Redis保持核心简单高效,同时将复杂性的控制权交给了能够做出最佳业务判断的程序员。