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

Redis里怎么一步步搞定求和操作,sum函数那些事儿聊聊

Redis本身没有一个像SQL里那样直接叫SUM的命令,你不能像在数据库里那样,对着一列数直接甩一个SUM(column_name)就完事儿,Redis处理求和,得根据你存数据的不同方式,用不同的招数来一步步实现,说白了,方法不对,努力白费”,所以选对数据结构是关键。

第一招:最简单直接的 – 用一个String键和INCR命令

这招适合的场景特别单纯:你只需要对一个数值进行不断的“加一”操作,统计网站的总访问量、文章的阅读数、用户点赞数等等。

  • 怎么存? 你就把它想象成一个普通的变量,初始化总访问量:SET total_visits 0
  • 怎么求和(其实是累加)? 每次有人访问,你就执行 INCR total_visits,这个命令会把total_visits的值增加1。
  • 怎么获取结果? 想看总数了,就用 GET total_visits

进阶一点:如果一次要加的不是1,而是指定的数呢? 比如统计总销售额,每次交易金额不同,这时候就用 INCRBY 命令,卖出一件100元的商品:INCRBY total_sales 100,卖出50元的:INCRBY total_sales 50,想减掉一笔退款?用 DECRBY,比如退款30元:DECRBY total_sales 30

这招的优点就是快如闪电,因为Redis处理这种简单的字符串数字操作是它的强项,但缺点也很明显:它只能对一个键进行求和,如果你需要统计不同类别的东西,比如同时统计“手机销量”和“电脑销量”,你就得为它们分别创建不同的键。

第二招:处理多个相关数值 – 用Hash和HMGET/HGETALL

当你的数据不是孤零零的一个数,而是一组相关的数据时,比如一个用户的多种积分(登录积分、购物积分、签到积分),用Hash结构就比用多个独立的String键要高效。

  • 怎么存? 用一个Hash键来存这一组值,比如存用户123的积分:HSET user:123:scores login 50 purchase 200 checkin 10,这里user:123:scores是键名,loginpurchasecheckin是字段(Field),后面的数字是值(Value)。
  • 怎么求和? Redis的Hash命令里没有直接求所有字段和的命令,这一步需要你“分两步走”:
    1. 先把所有值取出来: 可以用 HGETALL user:123:scores 把这个Hash里所有的字段和值都拿出来,或者用 HMGET user:123:scores login purchase checkin 只拿出指定的几个字段的值。
    2. 然后在你的应用程序代码里(比如用Python、Java、Go)把这些值加起来。 比如用Python伪代码:total_score = sum(int(score) for score in redis.hmget('user:123:scores', ['login', 'purchase', 'checkin']))

这招的优点是能很好地组织相关联的数据,取用方便,缺点就是求和计算的压力从Redis转移到了你的应用服务器上,如果Hash的字段非常多,一次性获取所有值可能会有点网络开销,对于这种“先取后算”的模式,有个小提示:如果求和操作非常频繁,你可以考虑用一个String键专门来存这个和,然后用INCRBY去维护它,相当于用空间换时间。

第三招:对付超大规模集合 – 用Sorted Set和ZUNIONSTORE

这是最强大但也相对复杂的一招,Sorted Set(有序集合)里的每个成员都有一个分数(Score),它本身是用来排序的,但我们可以利用它的分数来做求和。

  • 场景举例: 统计全网所有商品的总浏览量,商品有亿万个,每个商品ID对应一个浏览量。

  • 怎么存? 你可以把一个Sorted Set当作一个“大字典”,键是商品ID,值是它的浏览量(作为分数)。ZADD global:page_views 150 item:001 89 item:002 1000 item:003,意思是商品001有150次浏览,002有89次,003有1000次。

  • 怎么求和? 这里Redis提供了一个“类SUM”功能的命令,叫 ZUNIONSTORE(并集存储),这个命令的本意是把多个Sorted Set合并成一个新的Sorted Set,但它有个关键参数:AGGREGATE SUM,你可以这样用: ZUNIONSTORE temp_result 1 global:page_views AGGREGATE SUM

    • temp_result:存放结果的新键名。
    • 1:后面跟着的Sorted Set键的数量,这里我们只有一个。
    • global:page_views:我们要求和的源Sorted Set键。
    • AGGREGATE SUM:指定合并时对分数进行“求和”操作。

    执行完这个命令后,temp_result这个新的Sorted Set里,每个成员的分数其实就是它原来的分数(因为只有一个集合,合并就是自己)。那总浏览量在哪呢? 你需要再用一个命令:ZCARD temp_result?不对,ZCARD是看成员数量的,要看所有分数的总和,需要用 ZRANGE temp_result 0 -1 WITHSCORES 把所有人的分数都拿出来,然后像Hash那样在应用端再加一遍吗?那也太麻烦了。

    Redis没有直接返回Sorted Set总分数的命令,对于这种超大集合的求和,一个常见的做法是:

    1. ZUNIONSTOREAGGREGATE SUM 确保数据准备好在一個集合里(尤其是在多集合合并求和的复杂场景下,这步威力巨大)。
    2. 如果非要得到总和,可能需要借助Lua脚本(Redis允许在服务器端执行一小段自定义脚本),在Redis内部遍历成员分数进行累加,然后直接返回结果,避免大量数据传输,但这就要涉及到写脚本了,稍微高级一点。

在Redis里搞求和,没有万能钥匙,你得看你的数据是怎么放的:

  • 就一个数,不停地加? 用String和INCRBY,最简单最快。
  • 一小撮相关的数,想加起来? 用Hash存,用HGETALLHMGET取出来,在你自己程序里算。
  • 海量的键值对,需要汇总求和? 考虑用Sorted Set,利用ZUNIONSTORE的聚合功能做准备,复杂情况下可能需配合Lua脚本在Redis内部完成最终计算。

说白了,Redis给你的不是一把叫SUM的瑞士军刀,而是一个工具箱,里面有扳手(INCR)、螺丝刀(Hash命令)、电钻(ZUNIONSTORE),你得根据要拧的是螺丝还是螺母,来选择合适的工具,有时候甚至需要组合使用,希望这个一步步的讲解,能让你对Redis里的“求和”那些事儿有个清楚的认识。

Redis里怎么一步步搞定求和操作,sum函数那些事儿聊聊