TairZset 是阿里云自研的可实现任意维度 double 类型的分值排序的数据结构,借助 Tair 客户端同时可实现扩展性,即可以将计算任务分布至多个数据节点完成,实现分布式排行榜能力。本文介绍了 TairZset 的能力以及利用 TairZset 实现奖牌榜、分布式排行榜、实时排行榜的方案。

背景介绍

排序需求常见于各类游戏、应用、奖牌、积分等排行榜中,通常业务对排序的需求如下:

  • 支持增删改查和反向排序,可根据分数范围获取相应用户。
  • 排序操作执行速度快。
  • 支持多维排序。
  • 支持扩展(即 分布式排行榜 )。在数据分片容量或计算能力不足时,可以将其扩展到其他数据分片。

TairZset介绍

原生 Redis 支持的排序结构 Sorted Set(也称Zset)只支持一个 double 类型的分值排序,实现多维度排序时较为困难。例如通过IEEE 754结合拼接的方式实现多维度排序,此类方式存在实现复杂、精度下降、ZINCRBY命令无法使用等局限性,借助阿里云自研的 TairZset 数据结构,可帮助您轻松实现多维度排序能力,相较于传统方案具有如下优势:

  • 支持任意维度的 double 类型的分值排序(排序优先级为从左往右)。
  • 支持 ZINCRBY 命令,不再需要取回当前数据,在本地增加值后再拼接写回 Redis。
  • 支持和原生 Zset 类似的所有 API。
  • 客户端适配简易,无需任何编解码封装。
  • 提供 普通排行榜 和 分布式排行榜 能力。

通过 TairZset 实现奖牌榜

排名 参与方 金牌 银牌 铜牌
1 A 32 21 16
2 B 25 29 21
3 C 20 7 12
4 D 14 4 16
5 E 13 21 18
6 F 13 17 14

(注:数据仅做演示使用)

在奖牌榜中,首先按照金牌排名,如果金牌相等,则按照银牌,否则继续按照铜牌。在上述数据中,E 和 F 金牌数相等,但是银牌数 E 大于 F,因此 E 排名靠前,多维排序天然被 TairZset 支持,用户只需要使用简单的 API 即可完成。

1,首先依赖 Tair 客户端 alibabcloud-tairjedis-sdk

<dependency>
<groupId>com.aliyun.tair</groupId>
<artifactId>alibabacloud-tairjedis-sdk</artifactId>
<version>1.6.0</version>
</dependency>

2, 创建排行榜,乱序插入数据并排序。

JedisPool jedisPool = new JedisPool();
// 创建排行榜
LeaderBoard lb = new LeaderBoard("leaderboard", jedisPool, 10, true, false); // 如果金牌数相同,按照银牌数排序,否则继续按照铜牌
// 金牌 银牌 铜牌
lb.addMember("A", 32, 21, 16);
lb.addMember("D", 14, 4, 16);
lb.addMember("C", 20, 7, 12);
lb.addMember("B", 25, 29, 21);
lb.addMember("E", 13, 21, 18);
lb.addMember("F", 13, 17, 14); // 获取 A 的排名
lb.rankFor("A"); // 1 // 获取top3
lb.top(3);
// [{"member":"A","score":"32#21#16","rank":1},
// {"member":"B","score":"25#29#21","rank":2},
// {"member":"C","score":"20#7#12","rank":3}] // 获取整个排行榜
lb.allLeaders();
// [{"member":"A","score":"32#21#16","rank":1},
// {"member":"B","score":"25#29#21","rank":2},
// {"member":"C","score":"20#7#12","rank":3},
// {"member":"D","score":"14#4#16","rank":4},
// {"member":"E","score":"13#21#18","rank":5},
// {"member":"F","score":"13#17#14","rank":6}]

通过TairZset实现分布式排行榜

实现分布式排行榜有两种做法,分别为精确排名法和非精确排名法(线性差值法),下面分别介绍两种方法:

实现同一基本功能时,普通排行榜和分布式排行榜的实现方案如下:
说明 此处分布式排行榜采用精确排名法实现, 通过多个 TairZset 来保存数据在不同的分片上。

基本功能 普通排行榜 分布式排行榜
实现方案 时间复杂度 实现方案 时间复杂度
插入成员 通过 EXZADD 插入元素 O(N) 通过 crc(key) & m 计算要存放的目标 Key,然后通过 EXZADD 插入元素 O(log(N)) 说明 本列中的m代表分片数
更新成员分值 通过 EXZINCRBY 更新分数 O(log(N)) 通过 crc(key) & m 计算要存放的目标 Key,通过 EXZINCRBY 更新分数 O(log(N))
移除成员 通过 EXZREM 移除成员 O(M*log(N)) 通过 crc(key) & m 计算要操作的Key,然后通过 EXZREM 移除成员 O(M*log(N))
查询成员数量 通过 EXZCARD 查询成员数量 O(1) 分别通过 EXZCARD 查询成员数量,然后相加。 O(m)
查询总页数 通过 EXZCARD 查询成员数量,然后除以PAGE_SIZE(每页可展示的记录数) O(1) 分别通过 EXZCARD 查询成员数量,然后相加,得出的结果再除以PAGE_SIZE(每页可展示的记录数) O(m)
某分数范围内的总成员数 通过 EXZCOUNT 查询 O(log(N)) 分别执行 EXZCOUNT ,然后合并结果 m*O(log(N))
移除某分数范围内的成员 通过EXZREMRANGEBYSCORE移除 O(log(N)+M) 分别执行 EXZREMRANGEBYSCORE m* O(log(N)+M)
获取成员分值 通过 EXZSCORE 查询 O(1) 通过 crc(key) & m 计算要操作的 Key,然后执行 EXZSCORE O(1)
获取成员排名 通过 EXZRANK 查询 O(log(N)) 去每一个分片调用 EXZRANKBYSCORE,然后相加 m*O(log(N))
同时获取成员和排名 通过 EXZSCORE 和EXZRANK 查询 O(log(N)) 分别调用上面两个 API m*O(log(N))
查询第top i个成员 通过 EXZRANGE 查询 O(log(N)+M) 通过 EXZRANGE 查询每个 top i,然后合并并得出最终结果 m*O(log(N)+M)
获取排行榜第 i 页 通过 EXZRANGE 查询 O(log(N)) 将获取页数之前的元素都获取到,然后排序以获得最终的页数。如果查询的页面太靠后,可以限制获取的页面。 m*O(log(N))
设置过期时间 通过 EXPIRE 设置 O(1) 给每个元素设置过期 O(m)
删除排行榜 通过 DEL 删除 O(N) n 成员个数 同时 DEL 掉每个元素 m * O(n) m是key,n是成员个数

通过TairZset实现实时、小时、日、周和月维度的排行榜

该场景下的需求是实现月榜,那么这个Key就从月的维度进行索引,利用 TairZset 的多级索引能力可以轻松实现不同时间范围的排行榜。本案例中,月度的所有数据存储在一个 Key 中(名称为 julyZset),写入演示数据如下:

exzincrby julyZset 7#2#6#16#22#100 7#2#6#16#22_user1
exzincrby julyZset 7#2#6#16#22#50 7#2#6#16#22_user2
exzincrby julyZset 7#2#6#16#23#70 7#2#6#16#23_user1
exzincrby julyZset 7#2#6#16#23#80 7#2#6#16#23_user1

说明

  • 7#2#6#16#22#100表示7月第2周6号16点22分,更新其分数为100。
  • 7#2#6#16#22_user1表示此时间点更新的用户,用户名加入了具体时间前缀。

总结

本文介绍了常见排行榜方案,并介绍了 TairZset 的功能,实现了奖牌榜,分布式排行榜,以及时间维度的各类榜单,在性能高效的同时也帮助用户解决了实际的业务问题,欢迎大家使用交流。

参考:
[1] alibabacloud-tairjedis-sdk: https://github.com/aliyun/alibabacloud-tairjedis-sdk
[2] 线性差值法:https://zh.wikipedia.org/zh-hans/线性差值

好好的 Tair 排行榜不用,非得自己写?20 行代码实现高性能排行榜的更多相关文章

  1. 不写1行代码,在Mac上体验ASP.NET 5的最简单方法

    昨天微软发布了ASP.NET 5 beta2(详见ASP.NET 5 Beta2 发布),对ASP.NET 5的好奇心又被激发了. 今天下午在Mac OS X上体验了一下ASP.NET 5,而且借助Y ...

  2. 只写104行代码!在nopCommerce中如何实现自动生成网站地图

    表告诉我说你不知道nopCommerce是什么.它是目前.NET中最流行的完全开源网上商城,由俄罗斯的团队在2008年开始立项一直开发到现在已经是3.3版本了.代码目前托管在codeplex上,有兴趣 ...

  3. 厉害了,Google大神每天写多少行代码?

    文章转自开源中国社区,编译自:Quora Quora上有个有趣的问题:Google工程师们每天写多少行代码? Google 的 AdMob 全栈工程师 Raymond Farias 在 Quora 发 ...

  4. codeforces 876 D. Sorting the Coins(线段树(不用线段树写也行线段树写比较装逼))

    题目链接:http://codeforces.com/contest/876/problem/D 题解:一道简单的类似模拟的题目.其实就是看右边连出来有多少连续不需要换的假设位置为pos只要找pos- ...

  5. javascript写贪吃蛇游戏(20行代码!)

    <!doctype html> <html> <body> <canvas id="can" width="400" ...

  6. 爬数据,能让你少写1000行代码的捷径! | Python 正则表达式

    ▌春暖花开,又到了出门游玩拍拍拍吃吃吃的好季节了! 说到拍照摄影,你会构图吗?就是在照片有限的空间内处理人.景.物的关系,并将三者安排在画面中最佳的位置,以形成画面特定结构的方法. 学院君就是一个「拍 ...

  7. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  8. 瞧一瞧,看一看呐,用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!!

    瞧一瞧,看一看呐用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!! 现在要写的呢就是,用MVC和EF弄出一个CRUD四个页面和一个列表页面的一个快速DEMO,当然是在不 ...

  9. asp.net core 简化模型验证 modelState.IsValid不用每一个写

    第一种:直接在执行action之前验证模型 实现 IActionFilter public class ModelStateFilter : IActionFilter { public void O ...

随机推荐

  1. Git(1) - Git、Github和Gitlab简介

    Git是什么 概念 Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. SVN.CVS等,它们是集中式版本控制系统. 集中式和分布式版本控制 ...

  2. Spring Cloud Gateway 没有链路信息,我 TM 人傻了(中)

    本系列是 我TM人傻了 系列第五期[捂脸],往期精彩回顾: 升级到Spring 5.3.x之后,GC次数急剧增加,我TM人傻了 这个大表走索引字段查询的 SQL 怎么就成全扫描了,我TM人傻了 获取异 ...

  3. python 函数高级功能

    闭包 我们可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,且称之为外函数和内函数,外函数返回值是内函数的引用,此时就构成了闭包. # 闭包 # 外部函数的参数被内部函数引用,内部函数对外 ...

  4. vector 的交换技巧

    面试被问到如何解决 vector 有过多空闲内存的问题. 假定先有一 vector 容器 vec,它的容量是 10000,大小是 3. vector 的内存增长问题 vector 申请的是连续内存空间 ...

  5. VUE自学日志01-MVC和MVVM

    一.需要了解的基础概念 Model(M)是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开.这里的难点主要在于需要和前端约定统一的接口规则. View(V)是视图层,也就是 ...

  6. C++ostringstream用法

    ostringstream用法 1.类型转换 要求包含头文件; 字符串和int之间的互相转换: int num = 100; string str = ""; std::ostri ...

  7. 自然语言处理标注工具——Brat(安装、测试、使用)

    一.Brat标注工具安装 1.安装条件: (1)运行于Linux系统(window系统下虚拟机内linux系统安装也可以) (2)目前brat最新版本(v1.3p1)仅支持python2版本运行使用( ...

  8. 从零入门 Serverless | Serverless Kubernetes 应用部署及扩缩容

    作者 | 邓青琳(轻零) 阿里云技术专家 导读:本文分为三个部分,首先给大家演示 Serverless Kubernetes 集群的创建和业务应用的部署,其次介绍 Serverless Kuberne ...

  9. sql提示1055 不让你group by

    是不是突然写好的sql语句 部署上去就 Expression #2 of SELECT list is not in GROUP BY clause and containsnonaggregated ...

  10. 讲讲java中线程池的实现

    今天跟一个同学谈到java中的线程池的实现,才发现有些知识点已经记不清了,所以特意把源码打开,对官方文档做了些说明. 其实这些理解了之后,读懂源码应该是没多大问题了,有感兴趣的小伙伴们可以看完说明后自 ...