一种多维度排行榜实现方案

2,786 阅读6分钟

前言

排行榜是互联网中一种非常常见的需求,它能有效的激发用户的“攀比”欲望,达到提升用户活跃度和刺激用户消费的目的,比如直播间用户收送礼排行榜、用户贵族等级排行榜、游戏段位排行榜等。目前各类排行榜中常见的实现方案主要分为两种:基于DB(以MySQL为例)的方案和基于Redis方案。基于DB的方案是最简单的方案,通过DB支持的order by操作便可以轻松实现对榜单数据的排序,并且支持多个维度的排序,但其致命缺点在于DB操作涉及大量磁盘IO,速度很慢,仅适用于榜单数据量很小且访问频率较低的业务场景,在榜单数据量很大且访问频率很高的场景下无法适用,因此实际生产中这种方案采用比较少。基于Redis的方案则是使用Redis原生支持的zset数据结构,将榜单数据有序的存放到zset中,由于Redis操作都是内存操作,天然支持高性能,这种实现方式可以支持很大榜单数据量和访问量,可以满足大部分排行榜的场景,但原生zset只支持按元素分数值单个维度排序,因此在一些多维度的排行榜(比如奥运会奖牌榜单,需要按金、银、铜三种奖牌的数量排序)中也无法适用。

目的

本文仍基于Redis zset,并提出一种按维度权重计算分值的方案,将多个维度上的分值映射到zset单维度的分值上,实现对多维度排行榜的支持,解决原生Redis zset排行榜的不足,同时保持其原有高性能等优点。

实现

本文提出为不同的榜单排序维度分配不同权重并按权重计算最终得分值,排序优先级越高的维度权重值越大。以一个三维榜单为例,假设某个榜单需要按照指标1、指标2、指标3(分别对应维度1、维度2、维度3)排序,排序优先级为:指标1>指标2>指标3,三个维度的权重分别定为W1、W2、W3(权重都为正数,可以为小数),如果某个榜单项目三个指标得分分别为s1、s2、s3(单项指标得分都取正整数,如果有非整数得分可能,可以乘以10的指数倍),那么最终zset中存放的该项目得分为:

image.png

由于榜单需要优先按指标1得分s1排序,s1相同情况下再按指标2得分s2排序,s2相同的情况下再按指标3得分s3排序,因此对各个维度权重的选取必须满足以下约束条件:

image.png

其中max函数表示取该项得分的最大可能值,最后一个维度权重(即)视情况取一个合理常量值。上述权重约束条件目的在于使得高优先级排序维度上的得分更大程度上的影响最终得分S,并且高优先级维度上每加1分,其对最终得分S的影响比其它低优先级维度最大得分之和影响更大,这样可以确保s1越大则最终S越大,s1相同则s2越大S越大,s2相同才是s3越大S越大。因此最终Redis zset中存放的榜单数据一定是按指标1、指标2、指标3优先级排序的。

通过公式1可以根据榜单某个项目各项指标得分计算最终zset中存放的得分S,反之,知道zset中最终得分S,也可以反推该项目各项指标得分:

image.png

其中int函数表示取参数整数部分,注意单项指标得分s1、s2、s3都为正整数,否则上述公式不一定成立。

以上以一个三维榜单为例介绍了根据各维度得分按权重计算最终zset中榜单得分的方法以及根据zset中榜单得分反推各维度得分的方法。但本发明提出的方案理论上可以扩展到任意维度的榜单,假设一个N维榜单,分别需要按照维度1、维度2、维度3至维度N排序,各维度权重取W1、W2、W3 ... Wn,各维度得分为s1、s2、s3 ... sn,容易证明公式1、公式2、公式3可以扩展为:

image.png

image.png

image.png

其中最高维度(也即排序优先级最低的维度)权重视情况取一个合理常量值C。由于zset中存放的元素分数表示范围为-(2^53)到+(2^53),需要避免最终计算出的得分S溢出,所以C可以考虑尽可能小一些,必要时可以取小于1的小数,其他维度的权重值也尽可能取约束范围内最小值。

按上述方案,多维度排行榜维护流程如下:

image.png

以奥运会奖牌榜单为例,这是一个需要按金、银、铜数量排序的榜单,假设奥运会总共有100个项目,那么:

① 榜单维度为3,各个维度(金、银、铜)得分最大值均为100。

② 最高维度权重定为1,则按权重约束公式可取

现假设有A、B、C、D是个国家获得奖牌数量如下:

最终得分S
A25155256545
B201212205244
C201023205053
D201020205050

按扩展公式1计算各个国家最终得分如最后一列所示,排名依次为A、B、C、D,符合预期。同时以A最终得分为例,按扩展公式3可推出A的金、银、铜牌数量分别为:

金牌数=int(256545/10201)=25

银牌数=int((256545-25*10201)/101)=15

铜牌数=int((256545-2510201-15101)/1)=5

总结

本文提出了一种基于权重的多维度排行榜实现方案,通过为不同排序维度分配不同的权重值,并给出按维度得分与最终榜单得分互推方法,以及维度权重取值范围约束,轻松基于原生Redis zset实现多维度排序,有效利用Redis的性能优势,并解决直接使用zset实现排行榜需求的不足。理论上,只要各维度权重合理取值,本发明提出方案可以实现任意维度的排行榜。