0%

redis多维度排序问题

前言

  之前面试有被问到一个问题:小程序榜单按照点赞数降序排列,若点赞数一样,则按照最
新点赞时间降序排列。
  当时没有给出解决方案,现在突然有点想法,记录一下。

mysql排序

在网上看到过一种比较简单的解决方法:直接从数据库查询的时候,在sql中用order by进行排序。
mysql是支持多维度排序的,然后将查询结果放入redis即可。
不过这种方案限制比较多,因为排序是通过mysql查询实现的,那么每一次点赞操作都需要查一次库更新缓存。
这显然与我们使用redis缓存榜单数据的初衷背离了。

问题分析

放弃mysql查询排序的方案后,就只能将希望全放在redis上。
由简单到复杂,依次考虑以下几种情况:

  1. 单维度:
    如果是只按照点赞数排序,那么很简单,使用redis的zset类型,将点赞数作为score存入缓存。
    这样,可以借助zset的相关操作很方便按照点赞数排序查询数据。而每次点赞操作同步更新缓存中的score值即可。

  2. 两个维度,但同升序或降序:
    这时候增加时间维度,但和点赞数一样降序排列。那么实现方案也比较容易,依然使用zset的
    score,这时候,就不能直接将点赞数赋值给score,因为时间也是影响因数。
    直接想到的方案是取两者之和,但这就有一个明显问题,时间戳的值太大了,
    例如:一个点赞数30,一个点赞数20,但后者的时间戳比前者大100,那么就会出现点赞数少的排在前面。不符合先按点赞数排序的要求。
    为了实现先比较点赞数再比较时间戳的效果,可以采取组合的方式,将点赞数放在高位,时间戳放在低位,组合成一个数字。
    数字的大小比较不就是从高位往低位依次比较的嘛。
    不过这里需要注意,因为时间戳以秒为单位的话也有十位,而score是double类型,最高16位,
    可以采取将时间戳放在小数位或者设定一个比较接近的时间点为起始时间点计算时间戳等方法避免点赞数过大时超出score范围。

  3. 两个维度,但不同的排序要求:
    如开篇一样,增加时间维度,但为降序。那么就是点赞升序,时间降序。
    有了上述方案,可以照葫芦画瓢,修改下对时间戳的处理即可。
    这里定义一个未来的时间点为基准,取点赞最新时间点与其的差值,当点赞最新时间越晚(即时间戳越大),其差值便越小。
    然后依次将点赞数放在高位,差值放在低位组合成数字当作score存入缓存即可。

  4. 如果有更多维度的话,依旧按照维度的权重从高位放到低位,只不过需要注意,如点赞数这种会
    不断增加的数据,增加一些处理控制他的位数,避免最后计算的结果超过了double的范围。

总结

现在来看,这个问题并不复杂,关键思路是怎么设置维度数据的权重,有了权重的设置,将计算的结果当作score即可。