商业新闻

商业新闻
首页 > 新闻资讯 > 商业新闻 > 正文内容

大流量实战分享:如何高效的设计一个榜单?

时间:2023-11-18 信途科技商业新闻
业务场景

之前在一家直播团队做过一段时间的营收部门负责人,榜单是直播平台最通用的一种玩法,可以彰显用户的身份,刺激用户之间的pk,从而增加平台的营收,下面介绍几种榜单常见的玩法。

限时热门榜

玩法规则大致是每30分钟,对主播收到打赏值进行排行,其中有2类排行榜,限时热门总榜和限时热门分区榜,这里使用自然30分钟代表每个周期,每天有48个30分钟,分别有1、2、3代表每天第1、2、3个30分钟。

欧皇主播榜

玩法规则大致是主播房间内用户抽到的冰晶城堡数量的排行,页面上有3个榜单,昨日榜、今日榜、总榜。

直播重营收,营收看活动,活动看打榜,所以这种榜单每个月都会以各种形式出现,我们需要设计一套通用的榜单系统,减轻后续工作量,这是背景。

榜单分析

首先我们对业务进行抽象:

我们抽象出一些关键词:

用户id(user_id)主播id(master_id)投喂(coin)时间分区

时间有今日、昨日、自然30分钟。从这些榜单中我们可以抽象出统一的一套规则,榜单类型、榜单维度、榜单对象、榜单积分。

榜单规则榜单类型

同一种榜单类型代表的是一类榜单,这一类榜单具备同一套逻辑规则,例如限时热门榜,虽然每30分钟会有一个榜单,但是这些榜单数据的规则是一致的。

限时热门分区榜和限时热门榜的规则是不一样的,热门分区限时榜统计的是分区的主播,限时热门分区榜统计的是全区的主播。

需要注意的是,限时热门分区榜和限时热门榜也可抽象成一类榜单。

榜单维度

同一类榜单可能会有多个榜单,例如限时热门榜,每个自然30分钟内都会有一个榜单,每个的榜单都是不同的,或者说是互不影响的。

限时热门分区榜,每个自然30分钟内都会有一个榜单,这里自然30分钟就是一个维度。

限时热门分区榜,每个自然30分钟内*所有分区都会有一个榜单,这里自然30分钟和分区就是一个维度。

欧皇主播日榜,活动时间内主播房间内每日用户抽到的冰晶城堡数量的排行,这里日就是一个维度。

欧皇主播日榜,活动时间内主播房间内用户抽到的冰晶城堡数量的排行,这里只有一个榜单数据,维度为空。

榜单对象

榜单对象指的是我们给谁进行排行,这个谁可以是用户,也可以是主播,也可以是其他,例如限时热门榜,这个榜单对象就是主播,我们需要给主播进行排行。

榜单对象积分

榜单对象积分比较简单,就是一个进行排序的值,例如限时热门榜,用户消费就是积分。

榜单实现榜单配置

配置可以放在配置文件里面,或者可以通过后台管理系统进行管理,配置如下:

[[rank]]rankname = "master_luck_day" // 榜单类型title = "欧皇主播日榜" // 榜单名称,实际业务中没有使用到,这里只做一个名称区分top = 100 // 榜单最多展示n条,和业务有关set = 86400 * 2 // redis set的过期时间,见下方说明string_expire = 86400 // redis item的过期时间,见下方说明customsort = 1 // 自定义排序规则,代表相同积分,先到的在前,见下方说明[[rank]]rankname = "master_luck_total"title = "欧皇主播总榜" top = 100 set = 86400 * 30 // 假设活动过期时间是30天string_expire = 86400 customsort = 2榜单接口

这里只展示最常见的3个接口,其它接口请在具体业务场景中添加。

incrScore:增加榜单积分,类似于redis的incr;

请求参数

返回结果

{ "code": 0, "errcode": 0, "message": "ok", "errmsg": "ok", "data": { // 成功或失败,失败可以重试 "status": true }}

getScore:获取榜单分数及榜单排名;

请求参数

返回结果

{ "code": 0, "errcode": 0, "message": "ok", "errmsg": "ok", "data": { // 分数 "score": 0, // 排名 "rank": 0 }}

topScore:获取榜单排名

请求参数

返回结果

{ "code": 0, "errcode": 0, "message": "ok", "errmsg": "ok", "data": { // 排名数据 "data": [ { // rank_item "rank_item": 0, // 排名 "rank": 0, // 积分 "score": 0 } ] }}榜单表设计

表设计如下,在实际使用中,需要注意分库分表,索引也根据实际使用到的场景进行添加,这里只展示唯一索引:

CREATE TABLE `rank` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `rank_name` varchar(30) NOT NULL DEFAULT '0' COMMENT '榜单类型', `rank_type` varchar(50) NOT NULL DEFAULT '' COMMENT '榜单维度', `rank_item` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '榜单对象', `score` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '积分', `extra_data` varchar(50) NOT NULL DEFAULT '扩展数据', `rank` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排名', `custom_sort` varchar(200) NOT NULL DEFAULT '' COMMENT '自定义排序', PRIMARY KEY (`id`), UNIQUE KEY `uk_rank_id_rank_type_rank_item` (`rank_id`,`rank_type`,`rank_item`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通用榜单表'CREATE TABLE `rank_log` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `rank_name` varchar(30) NOT NULL DEFAULT '0' COMMENT '榜单id', `rank_type` varchar(50) NOT NULL DEFAULT '' COMMENT '子榜id', `rank_item` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '对象id', `msg_id` varchar(150) NOT NULL DEFAULT '' COMMENT '消息', `change_score` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '变化的积分', `after_score` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '变化后的积分', PRIMARY KEY (`id`), UNIQUE KEY `uk_rank_name_rank_type_rank_item_msg_id` (`rank_name`,`rank_type`,`rank_item`,`msg_id`),) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='榜单更新日志表'

更新榜单积分时,同时会更新榜单日志表,通过事务更新,保持数据一致性,通过msg_id保证幂等,接口如果调用失败,可以重试,类似于用户花钱时,会更新钱包数据同时会记录流水数据。调用incr接口时,会执行下面的sql,这2条sql在同一事务中执行。

insert into rank(rank_name,rank_type,rank_item,score) values(params.rank_name,params.rank_type,params.rank_item,params.score) insert on dumplicate update score = params.score;insert into rank_log(rank_name,rank_type,rank_item,score,msg_id) values(params.rank_name,params.rank_type,params.rank_item,params.score,params.msg_id);

需要注意的是数据库会保存全量排行榜数据。

事务说明

使用事务更新是否有必要?能否直接通过缓存做幂等?

确实在一般情况下使用缓存做幂等(set key ... nx px),然后辅以日志查询就足够了,使用流水日志对一致性更好,同时查询问题更加方便,但是对数据库的压力更大,可以根据实际业务场景选用合适的技术方案。

榜单缓存设计

在一般业务中,榜单只需要展示topn的排名数据,例如top10,top100等,并且在有一定体量的公司中,数据库都不能直接对外,必须在数据库上层加一层缓存。

榜单排名数据

榜单排名数据使用的是zset实现,zset的key为榜单名称+子榜id, zset的member为对象id,score为榜单积分。更新榜单时,做如下操作:

_rankListKey = "rank:list:%d:%s"rankListKey := fmt.Sprintf(_rankListKey, params.RankName, params.RankType)// 下面的redis操作可以使用一些优化手段,例如pipline,此处为示例redis.zAdd(_rankListKey, score, rank_item) // score代表的是该榜单对象当前的积分redis.Expire(_rankListKey, config.set_expire) // config.set_expire为配置set的的过期时间redis.zrembyscore(_rankListKey,0,last_rank_score - 1) //last_rank_score代表的是第top名的积分,删除0到最后一名之间的数据,保证数据只有top个

zset的过期时间大于榜单更新最大时间,如下所示:

需要注意的是,zset的member数量是需要限制的,不然可能会有大key和热key的问题。

榜单积分数据

业务场景中需要展示某个主播具体的有多少积分。榜单排名数据使用的是string实现, key为榜单类型+榜单维度+榜单对象,value为榜单积分。

此处可能会有人会有疑惑,为啥会需要需要榜单积分缓存?

zset限制member数量大小;业务场景需要展示超过topn的积分,如上第2张图;_rankItemKey = "rank:item:%d:%s:%d"rankItemKey := fmt.Sprintf(_rankItemKey, params.RankName, params.RankType, params.RankItem)score, err := redis.get(rankItemKey)if err == redis.ErrNil { // 回源数据库,查询积分,得到rscore redis.set(rankItemKey, rank_item, rscore + params.score, config.string_expire) // config.string_expire为配置的的过期时间 err = nil return nil} else if err != nil { // 返回错误,业务可以重试 return err}redis.incr(rankItemKey, params.rank_item,params.score)

榜单积分缓存数据量会比榜单排名缓存多很多,过期时间可以根据redis服务容量进行配置,可以在榜单更新时间内失效。

最后给一个流程图:

榜单更新流程

榜单实现案例限时热门榜/限时热门分区榜实现

当用户在直播间消费时,增加榜单数据,参数入下:

欧皇主播日榜/欧皇主播总榜实现

当用户在直播间抽奖抽到指定道具时,增加榜单数据,参数如下:

进阶场景近7日榜的实现

主播近七日收到用户打赏之和的排行,这里近七日是一个滑动窗口概念,例如20200420代表的是20200414 ~20200420这7日。

业务分析

榜单维度,可以用日期来标识,例如20200420代表的是20200414 ~20200420这7日 榜单对象,主播 榜单积分,主播近7日收到的积分之和

方案1

存在两种榜单数据,一个是七日的榜单数据(实际使用),一个是每日的榜单数据(辅助使用)。

每日凌晨启动定时任务将前6日的日榜数据加到近7日的榜单数据中,数据是从数据库中获取,获取的是全量数据,当凌晨用户投喂时,会实时更新七日榜单的数据,也就是说脚本积分数据和实时积分数据是同时在跑的,理论上,当脚本跑完时,数据会是正确的。

这种方案好处是简单,可以快速实现,坏处需要定时任务,且数据不是平滑更新的,定时任务执行期间数据不准确。

方案2

方案2没有使用每日的辅助榜单数据,每次更新数据时会同步更新今日的七日榜和后6天的七日榜,例如今天是2022-04-20,如果增加1积分,会同时更新20220420七日榜、20220422七日榜、20220423七日榜、20220424七日榜、20220425七日榜、20220426七日榜。

当到了26日时,主播1的20220426七日榜的积分会为3;当到了27日时,主播1的20220427七日榜的积分会为2;当到了28日时,主播1的20220428七日榜的积分会为1;当到了29日时,主播1的20220429七日榜的积分会为0。

这种方案好处是没有定时任务,数据是平滑更新的,坏处是接口请求会放大,同时会更新很多条数据,基本无法支持近30天的场景,且业务调用较为复杂。

方案3

更新数据时更新今日的七日榜数据,同时更新明天的七日榜数据(如果没有脚本相当于是今日的日榜数据),并且记录每日的数据,每日中午会将前5日每日的数据加到明日的7日榜中。

我们一起看一下20220423七日榜的数据的正确性,20220423七日榜在2022-04-22增加积分1,在2022-04-22中午,将2022-04-17 ~ 2022-04-21这5天日榜的数据共2分加到了20220423七日榜中,在2022-04-23主播1增加1积分增加了积分1,主播积分为4。

这个方案的好处是数据是平滑更新的,可以实现任意时间阶段的连续榜单,且调用简单,连续榜的逻辑已是在服务内部实现,坏处是实现较为复杂。

榜单积分相同如何排序?

zset存在一个问题,就是相同积分时,zset会按照member的字典序进行排序,有些业务场景,可能会对相同积分的也需要进行排序,例如相同积分,先到在前。榜单配置中增加有customsort字段, 1代表按时间正序排序, 2代表按时间倒序排序。

数据库存在custom_sort字段,如果按照时间正序排序,为负数的时间戳,如果按照时间倒序排序,为正数的时间戳。

每次更新积分数据后,搜索数据库与该对象积分相同的数据(最多top条,根据配置,下面用1000来说明),sql语句为:

select item_id from rank where rank_name = params.rank_name and rank_type = params.rank_type and score = cur_score order by custom_sort desc limit 1000

然后将score积分加上一个小数,从0.999至0,将相同的数据添加至zset之中,从而实现相同积分排序。

如何实现排名变化趋势?

有些榜单场景会有主播今日的排名会和逐日昨日的排名进行比较,看是上升、下降还是不变?

例如主播今日投喂榜需要实现排名变化趋势,可以每天零点执行脚本,获取榜单上一个周期的排行数据,也就是昨日的topn排名的主播排行信息,写到今日的榜单数据中,并且将昨日排名数据,写到今日的排行数据中,字段使用extra_data,当获取榜单排行时,可以获取到extra_data数据,当前排名和昨日排名数据进行比较即可得到变化趋势,若没有获取到extra_data数据,即昨日没有上排行榜,变化趋势为向上。

这个方案有个小问题,就是不够平滑,但该功能实时性要求较小,可以忽略。extra_data怎么使用缓存、怎么平滑展示数据留个大家去思考。

以上就是一个实际业务场景,以及面对这个业务场景时候如何提升开发效率的case。

好了,今天的分享就到这,喜欢的同学可以四连支持:

本人花费2个月时间,整理了一套JAVA开发技术资料,内容涵盖java基础,分布式、微服务等主流技术资料,包含大厂面经,学习笔记、源码讲义、项目实战、讲解视频。

java面试资料

希望可以帮助一些想通过自学提升能力的朋友,领取资料,扫码关注一下

记得转发+关注+私信

私信回复【2022面试资料】

领取更多学习资料

扫描二维码推送至手机访问。

版权声明:本文由信途科技转载于网络,如有侵权联系站长删除。

转载请注明出处https://www.xintukeji.cn/xintu/73055.html

相关文章

网络营销网站推广途径(网络营销和网站推广的差别)

1、手机网站(对外展示公司产品和联系方式)2、微信公众平台(定期发布公司动态和最新产品)3、小程序(定期推出公司营销类活动,比如投票,表单手机,抽奖等)4、抖音号(公司认证抖音号,发布公司聚会,发布会...

关于五菱宏光网络营销策划方案的信息

为什么你写的方案又慢又总通不过?其实,那可能是因为,你没有一个营销方案库!今天社长要为小伙伴们带来这套2022五菱宏光MINI。明确做营销策划方案的工作原则在你真真正正准备去做一份营销策划方案之前,你...

网络营销的推广工具(网络营销的推广工具和方法)

网络推广工具_产品 而是在那一道道许可营销目光的注视早教课程推广文案下,将之上的火莲推广软件网络推广软件作品工具,推广场的人,这些家伙我也视。ProcessOn和OmniGraffle也是较为常用的流...

武汉网络营销整合平台咨询的简单介绍

旅游企业怎么做好网络营销推广01营销能力是旅行社成功的第一步我们虽然做了十多年的旅游行业,但是一直觉得还是有点边缘,可能。立体网络营销的打造,进一步增强了宝武水务市场竞争力,通过协同合作实现资源的高效...

外国人在中国做网络营销的简单介绍

中国顾客网络行为的特点 在中国,一个好的数字营销策略应该创建在对 中国顾客互联网行为 的深刻理解之上到现在为止,游戏规矩与西方的规矩并不相。从美国人的网络营销中,我们可见一斑 用电子邮件将网站的参观者...

58同城关键词排名(58同城关闭简历)

编辑导语:如今,“种草”产品这一赛道竞争激烈,大厂也来参与。那么,会有另一个“小红书”出现吗?目前小红书有什么困境,又该如何解?本文就此问题做了分析和解答,一起来看看吧。种草赛道动作频频。据《晚点La...

现在,非常期待与您的又一次邂逅

我们努力让每一次邂逅总能超越期待

  • 效果付费
    效果付费

    先出效果再付费

  • 极速交付
    极速交付

    响应速度快,有效节省客户时间

  • 1对1服务
    1对1服务

    专属客服对接咨询

  • 持续更新
    持续更新

    不断升级维护,更好服务用户