使用redis的有序集合实现排行榜功能 - 简书

标签: | 发表时间:2018-11-12 14:41 | 作者:
出处:https://www.jianshu.com

排行榜是业务开发中常见的一个场景,如何设计一个好的数据结构能够满足高效实时的查询,下面我们结合一个实际例子来讨论一下。

场景

选手报名参加活动,观众可以对选手进行投票,每个观众对同一名选手只能投一票,活动期间最多投四票。后台需要提供如下接口:

  • 接口1:返回TOP 10的选手信息及投票数
  • 接口2:返回活动总参与选手数及总投票数
  • 接口3:对于每个选手,返回自己的投票数,排名,距离上一名差的票数

基于数据库的方案

首先需要一张表存储投票记录,一次投票就是一条记录。这张表相当于投票明细,判断每人只投一张票以及最多投四张表都依赖对这张表的查询。
如果直接对这张表做TOP 10的查询,则需要根据选手id做聚合查询,这样每次查询必然耗时。为了优化查询,可以增加另一张排行榜表,用一个定时任务每隔一段时间对原表做聚合查询,然后将结果写进排行榜表里,表里包含投票数及排名的字段,这样查询TOP 10和排名的时候直接查这张表。引入另一张表加快了性能,但牺牲了实时性,活动说明里需加上类似“榜单数据每10分钟同步一次”的话来告知用户。

基于redis的方案

对于排行榜的需求,redis有一个数据结构非常适合做这件事,那就是有序集合(sorted set)。

redis的有序集合相关命令

有序集合和集合一样可以存储字符串,另外有序集合的成员可以关联一个分数(score),这个分数用于集合排序。下面以投票为例说明常见的命令,vote_activity是有序集合的key。

      #给Alice投票
redis> zincrby vote_activity 1 Alice
"1" 
#给Bob投票
redis> zincrby vote_activity 1 Bob
"1"
#给Alice投票
redis> zincrby vote_activity 1 Alice
"2"
#查看Alice投票数
redis> zscore vote_activity Alice
"2"
#获取Alice排名(从高到低,zero-based)
redis> zrevrank vote_activity Alice
(integer) 0
#获取前10名(从高到低)
redis> zrevrange vote_activity 0 9
1) "Alice"
2) "Bob"
#获取前10名及对应的分数(从高到低)
redis> zrevrange vote_activity 0 9 withscores
1) "Alice"
2) "2"
3) "Bob"
4) "1"
#获取总参与选手数
redis> zcard vote_activity
(integer) 2

接口实现

回到最开始的场景,大部分需求都已经得到满足,还剩下两个数据需要单独说一下。接口2中的总投票数没有直接的接口获得,一种方法是先用 ZRANGE遍历所有的key,然后对score进行求和,另一种方法是对总票数单独用一个数据结构存储。接口3的距离上一名差的票数,先用 ZREVRANK获取自己排名,然后用 ZREVRANGE获取上一排名的分数,最后用自己的分数减去上一名的分数即可,代码示例如下:

      def get_next_step(redis_key, member):
    next_step = None
    score = redis.zscore(redis_key, member)
    rank = redis.zrevrank(redis_key, member)
    if rank > 0:
        next_member = redis.zrevrange(redis_key, rank - 1, rank - 1, withscores=True)
        next_step = next_member[0][1] - score
    return next_step

另外如果两个key的score相同,排序逻辑是按照key的字母序排序。在有些情况下这个可能不满足实际要求,因此需要按实际情况重新设计key。比如如果要求同分数情况下按时间排序,那么key最好加上时间戳前缀。

redis与数据库的同步

redis通常是作为缓存层加速查询的,如果数据没有做持久化则有概率会丢失数据。一个方案是用定时任务定时同步redis与数据库的数据,数据库里存储着原始数据,通过计算数据库的数据和redis做对比,可以修正由于redis不稳定导致的数据不一致。这里需要注意的是在同步过程时redis的数据有可能还在增长,因此最好先读redis的数据,然后记下时间,查询指定时间段里的数据库的数据,最后再用 ZINCRBY增量修正redis数据,而不是直接用 ZADD覆盖redis数据。

总结

redis的有序集合是一个非常高效的数据结构,可以替代数据库里一些很难实现的操作。它的一个典型应用场景就是排行榜,通过ZRANK可以快速得到用户的排名,通过ZRANGE可以快速得到TOP N的用户列表,它们的复杂度都是O(log(N)),用来替代数据库查询可以大大提升性能。

相关 [redis 有序集合 功能] 推荐:

使用redis的有序集合实现排行榜功能 - 简书

- -
排行榜是业务开发中常见的一个场景,如何设计一个好的数据结构能够满足高效实时的查询,下面我们结合一个实际例子来讨论一下. 选手报名参加活动,观众可以对选手进行投票,每个观众对同一名选手只能投一票,活动期间最多投四票. 接口1:返回TOP 10的选手信息及投票数. 接口2:返回活动总参与选手数及总投票数.

利用Redis的有序集合做购物车商品相关性分析

- - zzm
本文所指的“商品的相关性”,就是依据与某个商品同时出现在购物车中次数最多的商品. 在某一商品的detail页面,推荐给用户与该商品相关的N个商品;. 在添加购物车成功页面,当用户把一个商品添加到购物车,推荐给用户N个与之相关的商品;. 在货架上将相关性比较高的几个商品摆放在一起;. 利用Redis的有序集合做法如下:.

Redis的AOF功能

- - CSDN博客数据库推荐文章
引言:  Redis是基于内存的数据库,同时也提供了若干持久化的方案,允许用户把内存中的数据,写入本地文件系统,以备下次重启或者当机之后继续使用. 本文将描述如何基于Redis来设置AOF功能. AOF是AppendOnly File的缩写,是Redis系统提供了一种记录Redis操作的持久化方案,在AOF生成的文件中,将忠实记录发生在Redis的操作,从而达到在Redis服务器重启或者当机之后,继续恢复之前数据状态的机制.

Redis集群功能说明

- gOODiDEA - NoSQLFan
虽然目前可以通过在客户端做hash的方法来构建Redis集群,但Redis原生的集群支持还是颇受期待. 本文是对Redis集群功能官方描述文档的一个翻译,译者是@PPS萝卜同学,也感谢他的投稿分享. 这篇文档主要是为了说明正在进展中的Redis集群功能. 文档主要分为两个部分,前一部分主要介绍我在非稳定分支已完成的代码,后一部分主要介绍还有哪些功能待实现.

REDIS GEO: REDIS新增位置查询功能

- - 编程语言 - ITeye博客
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/144.html. 移动互联网增进了人与人之间的联系,其中基于位置信息的服务(Location Based Service,LBS)起到很重要的促进作用. 在移动互联网的大环境下,每个手机都变成了一个位置追踪设备,为人们提供了非常丰富的位置服务.

Redis 负载监控——redis-monitor

- - ITeye资讯频道
redis-monitor是一个Web可视化的 redis 监控程序. 使用 Flask 来开发的,代码结构非常简单,适合移植到公司内网使用. redis 服务器信息,包括 redis 版本、上线时间、 os 系统信息等等. 实时的消息处理信息,例如处理 command 数量、连接总数量等. 内存占用、 cpu 消耗实时动态图表.

redis实现高并发下的抢购/秒杀功能 - 周伯通的麦田 - 博客园

- -
高并发的解决思路(点此进入查看),今天再次抽空整理下实际场景中的具体代码逻辑实现吧:. 抢购/秒杀是如今很常见的一个应用场景,那么高并发竞争下如何解决超抢(或超卖库存不足为负数的问题)呢. 查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数.

使用MQTT协议+Redis缓存实现APP登录顶号功能 | jwcqc个人笔记

- -
大家在玩游戏或使用QQ等IM工具时,想必都见到过弹出被顶号或者是您的账号于xx时间在另一设备登录,您已被迫下线这样的提示,然后不得不点退出按钮退出整个应用,或者点击重新登录把另一设备再顶下来. 最近我参与的一个项目,正好就有这样的需求,而且,由于我们项目中已经使用到了MQTT协议进行消息推送,实现远程控制,后台用Java实现,缓存使用了Redis,因此,正好可以利用现有的技术来实现这个功能.

Redis 起步

- - 博客园_首页
Rdis和JQuery一样是纯粹为应用而产生的,这里记录的是在CentOS 5.7上学习入门文章:. Redis是一个key-value存储系统. 和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型.

redis 配置

- - 谁主沉浮
# 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写). # 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB. # daemonize no 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes. # 当redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid,你可以配置到其他地址.