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

Redis那玩意儿,怎么能快查字段,实操分享给你看看

Redis那玩意儿,怎么能快查字段,实操分享给你看看 基于知乎专栏“Redis实战笔记”和开发者社区“掘金”上的多篇用户实践文章综合整理)

好多人都知道Redis快,但真到自己用的时候,可能就只会setget,顶多再用个list,想根据内容里的某个字段快速查数据,第一反应可能就是全捞出来自己在程序里过滤,那可就太浪费Redis的性能了,今天就直接上实操,看看怎么在Redis里玩转字段查询。

最简单的招数——用哈希(Hash)和主键

这是最直接的方法,比如我们要存用户信息,用户ID是1001,他有名字、年龄、城市这些字段。

  • 实操命令:

    HMSET user:1001 name "张三" age 28 city "北京"

    这就创建了一个键为user:1001的哈希结构,里面存了三个字段。

  • 怎么快查?

    • 查整个用户信息: HGETALL user:1001,一把全捞出来。
    • 只查名字(单个字段): HGET user:1001 name,只取你要的,省网络传输。
    • 只查年龄和城市(多个字段): HMGET user:1001 age city
  • 关键点: 这种查询快在哪?快在它是直接通过键(user:1001)来定位的,Redis的键就相当于关系数据库的主键,用哈希表实现的,查找速度是O(1),几乎瞬间完成。如果你的查询条件总是某个对象的主ID,用Hash结构是最合适的。

  • 局限: 但如果我想找出所有在北京的用户怎么办?用HGETALL把所有用户数据都拉出来,再在程序里过滤?用户量一上来,这操作简直灾难,这就需要下面的方法了。

给字段值建个“反向索引”——集合(Set)的妙用

Redis没有像MySQL那样的条件查询语句,但我们可以自己手动建“索引”。

  • 实操思路: 我们还是用Hash存用户详细信息,我们额外维护一些集合(Set),这些集合的键和字段值相关,成员(member)就是对应的用户主键。

  • 操作步骤:

    1. 存用户主数据(和上面一样):
      HMSET user:1001 name "张三" age 28 city "北京"
      HMSET user:1002 name "李四" age 25 city "上海"
      HMSET user:1003 name "王五" age 28 city "北京"
    2. 为“城市”字段建索引:
      SADD index:city:北京 1001 1003
      SADD index:city:上海 1002

      看,我们创建了两个集合键index:city:北京index:city:上海,里面存放的是所有属于该城市的用户ID。

  • 怎么快查? 现在要找所有在北京的用户,简单了:

    SMEMBERS index:city:北京

    这条命令会直接返回 10011003,然后我们再根据这些ID,用HMGETHGETALL去批量获取用户的详细信息,虽然要查两次,但第一次的SMEMBERS和后续的批量HGET都非常快,避免了全表扫描。

  • 关键点: 这其实就是空间换时间,多占点内存,维护这些索引集合,换来查询的飞速提升,对于需要频繁按某个字段查询的场景,非常有效。

需要排序和范围查询?上有序集合(ZSet)

如果查询条件不只是“等于”,还涉及范围,年龄在25到30岁之间的用户”,Set就不好使了,这时要用有序集合(Sorted Set)。

  • 实操思路: 我们为年龄字段建立一个有序集合索引,集合的成员是用户ID,分数(score)就是年龄值。

  • 操作步骤:

    1. 存用户主数据(同上)。
    2. 为“年龄”字段建ZSet索引:
      ZADD index:age 28 1001
      ZADD index:age 25 1002
      ZADD index:age 28 1003
  • 怎么快查?

    • 查年龄等于28岁的用户: ZRANGEBYSCORE index:age 28 28,虽然ZSet是按分数排序,但精确等于就是范围的特例。
    • 查年龄在25到30岁之间的用户: ZRANGEBYSCORE index:age 25 30,这个查询效率极高,因为ZSet底层是跳表+哈希表,按分数范围取成员很快。
  • 组合查询怎么办? 那更复杂点,我要找“年龄28岁且在北京的用户”呢?这就是组合查询,Redis本身不直接支持多索引的交集查询,但我们可以用SINTERSUNIONSTORE等命令来模拟。

    1. 先用ZRANGEBYSCORE index:age 28 28 得到年龄28的用户ID集合(假设为A)。
    2. 再用SMEMBERS index:city:北京 得到北京的用户ID集合(假设为B)。
    3. 最后在程序里求集合A和B的交集。 如果数据量巨大,可以在Redis服务端使用SINTER命令直接计算交集(SINTER setA setB),但要注意性能,更高级的做法是使用Redis的SUNIONSTORESINTERSTORE将中间结果存为临时键,但一般建议在客户端处理,避免阻塞Redis。

一些实在的提醒

  1. 内存是代价: 建索引很爽,但每个索引都是一个Redis的数据结构,都要占内存,你得权衡一下,是不是所有字段都需要建索引。
  2. 数据一致性: 这是个细活儿,你更新用户信息时,比如把user:1001从北京改成上海,必须同时更新主Hash数据(HSET user:1001 city "上海"两个索引集合(SREM index:city:北京 1001SADD index:city:上海 1001),漏一步,数据就脏了,通常把这堆操作包在一个事务(MULTI/EXEC)里,或者用Lua脚本来保证原子性。
  3. 不是万能药: Redis这么玩,对付简单的、结构固定的多字段查询还行,如果查询条件非常复杂、多变,或者数据关系错综复杂,那它还是比不上专门的关系型数据库或者搜索引擎(如Elasticsearch),别硬用Redis扛所有场景。

想让Redis快查字段,核心思想就俩:一是用好本身的数据结构(Hash, Set, ZSet),二是手动构建索引,实操起来不难,关键是理解这个思路,然后根据你的业务场景,灵活组合运用。

Redis那玩意儿,怎么能快查字段,实操分享给你看看