从零实现一个榜单

运营活动需要实现一个投票打榜的功能:用户通过做任务获取票,获得票之后点击页面的“投票”按钮,给主播加票;打榜页面会根据主播的得票数对主播进行排序,展示榜单前N名。

一、榜单存储设计

方案1:zset

对于上述榜单功能的设计,最简单的方式就是利用redis的zset结构存储榜单的排名,key为本次榜单的标识符,member存储主播的id,score存储主播的票数。但我们都知道zset会有个存储的数量限制,推荐的最大值是5000,如果说榜单排序的主播个数超过上述限制,那此时需要设计更为复杂的方案。

redis zset结构示意图

方案2:zset+k-v

如果参与榜单排名的主播数过多,只用zset存储容易导致大key问题,这时候需要搭配redis k-v存储所有主播的票数。具体解决方案如下:

1. 榜单只存储topN个主播的票数,比如榜单页面需要展示top100的主播票数,N=100即可;

2. 所有主播的票数单独存储,采用redis k-v存储票数。key为主播id,value代表主播的票数。

我们本次的功能因为主播数较多,因此采用方案2实现。

二、榜单更新

榜单更新方式

方案1:同步更新

同步更新是最简单的实现方式,在用户调用投票接口时直接更新主播的票数和榜单的排名,但这个方案直接访问redis,只适用于活动流量较小的情况。从稳定性的角度考虑,建议走下面的异步更新方案。

方案2:异步更新

考虑到活动过程中,容易出现突发流量,同步更新的方式从稳定性的考虑角度不建议,因此采用异步更新的方式来实现。

将用户投票的消息异步发送kafka,在consumer内异步处理给主播投票的消息,给主播加票,同时更新榜单排名。

三、数据一致性

榜单更新逻辑

基于zset+kv的实现方式,用户给主播投票,我们需要更新两个地方的数据

1. 更新主播在kv里的票数

2. 更新主播在zset的票数

上述的更新过程是两个操作,于是在实际更新榜单的过程中,可能会导致一些特殊case的出现,下面我们来分case具体分析下解决方案。

case1:并发更新导致的数据不一致

假设有两个用户A、B同时对主播进行投票+10票,主播的原始票数是10;两个操作的顺序有可能会导致数据不一致

并发更新榜单排名和主播票数

由图中可以看到,解决并发更新最简单的方式是将上述两步操作用lua脚本封装成原子操作,但是目前公司的redis使用规范不提供使用lua脚本,因为脚本复杂度不可控,容易造成一些难以定位原因的redis故障,我们尝试想一些别的方法。

主播更新票数主要分为两种情况,用户上榜时直接更新榜单内的票数,用户不上榜时更新kv数据,再用新的票数去更新榜单数据。于是最后的方案定位以下四步:

1、先假设主播在榜上,使用zIncrByParams().xx()指令直接更新榜单上的票数;如果成功,直接走步骤4;失败走步骤2;

2、失败说明主播不在榜上,查询主播当前票数+delta之后,使用zIncrByParams().nx()指令更新榜单上的票数;如果更新成功,说明此时没有别人更新,走步骤4;如果更新失败,说明此时有别人先一步让主播上榜,走步骤3;

3、步骤2失败,说明此时主播已经被更新到榜上了;重复步骤1,使用zIncrByParams().xx()指令直接更新榜单上的票数。

4、更新kv内的主播票数,zincrBy即可。

这个方案保证了主播在榜上更新票数的原子性,对于主播不在榜上的情况对票数更新加分布式锁,保证同时只有一个线程更新票数,能解决大多数情况下并发更新导致的数据不一致情况。

case2: 距离上一名-1

除了上述并发更新的问题,主播票数存储在两个地方,如何保证获取到的主播票数一致性是一个问题。这里踩了另外一个坑。

笔者在展示榜单排序时,以zset的排序为准获取主播的排序,但展示票数时请求了k-v内单独存储的票数。上述的实现在实际过程中,出现了一个意外的case:距离上一名的票数计算出来是-1。初始以为是redis更新k-v和更新zset时有失败的情况,但后续实际增加监控发现redis的可用性还是很高的,基本不存在这种情况。

在此假设基础上,最有可能的情况是在更新k-v和更新redis的中间请求了数据,导致两部分数据一致,出现了距离上一名票数-1的极端case。

最终的实现方案是,榜单页面展示时上榜主播的票数以zset内存储的为准,保证排名和票数获取数据来源的一致性,单独查询主播票数时以k-v内存储的票数为准,即可避免上述问题的出现。

四、风控和安全

榜单漏出的数据除了上述问题之外,还需要考虑数据的安全性,比如上榜主播昵称是否满足基本的风控要求,需要具有紧急下榜的能力。

五、榜单更新的实时性

榜单更新我们采用的是异步操作,那么前端页面展示部分,如何去保证榜单页面更新的实时性呢?

初始以为直接前端mock票数即可,后来发现榜单页面的更新,除了更新票数之外,还涉及到排名部分的更新,如何保证榜单页面实时展示给用户是个亟待解决的问题。

我们此处采用的是和前端配合的形式,后端接口榜单整个异步操作的延时基本可以保证在1秒以内,前端用户投票之后延迟1秒去请求接口,1秒之后请求的接口数据基本为新数据,这个过程中给用户的体验基本是实时更新了榜单页面。

六、定榜

榜单的话一般会涉及到一个定榜操作,对于榜单topN的数据会发放一些奖励等等,因此主播和观众对于榜单定榜的结果较为关注,定榜的实时性也是一个较为重要的问题。

针对上述投票打榜的活动,投票接口我们会限制只有活动时间内可投票。但因为我们选择的方案是异步更新榜单排序,用户的投票消息会延迟几秒后处理,所以我们的定榜时间要延迟于活动的结束时间。那如果消息延迟过大怎么办呢,迟迟不顶榜?

最简单的方式是设定一个最大延时定榜时间,异步处理consumer内增加系统时间判断,如果系统时间大于最大延时定榜时间的话,其他的投票消息不处理,即可达到定榜的结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容