排行榜设计-高并发场景下的最佳实践

排行榜设计-高并发场景下的最佳实践

项目背景

AI电力交易竞赛平台,需要为参与交易竞赛的团队设计一个的排行榜,按照不同的赛道进行排名,价格预测赛道按照多个准确率排名,交易赛道按照收益进行排名。具体需求如下:

实时性:市场边界变化时,排行榜要立即更新。

高并发:支持所有参赛团队同时查询排行榜(100个参赛团队)。

排名稳定性:排名计算准确,且能应对短时间内的大量更新。

设计思路

在设计排行榜系统时,关键要点在于明确实时性需求以及选择合适的数据存储和更新策略,这直接影响到系统的性能和用户体验。

实时性与存储时机

排行榜按实时性可分为实时排行榜和非实时排行榜,二者主要区别在于存储更新的时机:

实时排行榜:当市场边界或相关数据发生变化时,排行榜需立即更新。这要求系统能实时捕捉数据变动,并迅速在存储中反映出来,以确保用户随时获取到最新的排名信息。例如,在在线竞技游戏中,玩家的实时积分一旦改变,游戏的排行榜就应即刻更新。

非实时排行榜:允许在数据发生变化后延后更新。常见做法是借助定时任务框架,如 xxl - job 或 powerjob,按设定的时间间隔(如每小时、每天等)执行统计任务。任务执行时,从数据源查询相关数据进行统计计算,完成后将结果存储到相应的存储介质中。例如,电商平台的月度商品销售排行榜,可通过每天凌晨执行的定时任务,统计前一天的销售数据来更新排行榜。

数据存储与更新方案

在排行榜设计中,如何高效存储和更新排行榜数据是核心问题,常见方案如下:

数据库方案

原理:传统关系型数据库(如 MySQL)利用排序和索引功能实现排行榜。例如,假设数据库中有subject_info表,create_by字段为用户唯一标识,通过select count(1), create_by from subject_info group by create_by limit 0, 5;语句,可对用户进行分组统计,并获取排名前 5 的用户。

优势:数据存储具有完整性和一致性保障,数据持久化能力强,适用于数据准确性要求极高且数据量、并发量较小的场景。

劣势:在高并发场景下,数据库承受的查询压力巨大,若索引使用不当,易产生慢 SQL,导致查询效率低下,难以满足实时性需求。

优化措施:可在数据库层面添加缓存,以减轻数据库查询压力,提升响应速度,但会引入一定的延时性。

缓存方案:

原理:以 Redis 为代表的内存数据库,利用其有序集合(Sorted Set)存储排行榜数据。将排行榜项目作为 Sorted Set 元素,对应分数作为排序依据。如游戏排行榜,玩家 ID 为元素,游戏得分是分数,使用ZADD命令更新分数,Redis 自动重新排序,通过ZRANGE或ZREVRANGE命令查询排名。

优势:基于内存操作,性能卓越,查询速度极快,能满足高并发和实时性要求,且减少数据库负载。

劣势:需额外搭建和维护 Redis 环境,增加系统复杂性,使用不当易出现大 key 问题,影响性能和稳定性,且数据存在内存丢失风险(可通过持久化缓解)。

混合方案

原理:结合数据库和缓存。数据库用于数据持久化存储,保障数据安全可靠;Redis 负责实时计算和快速查询。例如,新数据先写入数据库,再同步到 Redis 更新排行榜,查询时直接从 Redis 获取。

优势:兼顾数据持久化和高效查询,既能满足实时性需求,又能保证数据完整性,适用于对实时性和数据可靠性都有较高要求的场景。

劣势:系统架构相对复杂,需要协调数据库和缓存之间的数据同步,增加了开发和维护成本。

方案选择

综合考虑系统的实时性和高并发需求,推荐以 Redis 作为排行榜的核心存储,并配合 MySQL 进行数据持久化。Redis 的有序集合为排行榜排序和排名操作提供高效实现,外部交互直接访问缓存,可大幅提升系统性能,同时 MySQL 确保数据安全存储,满足大多数场景下的需求。

具体实现

数据结构设计

在Redis中,我们可以使用Sorted Set来实现排行榜。Sorted Set是一个带有分数的集合,集合中的每个元素都有一个唯一的值和一个关联的分数。我们可以利用分数进行排序,从而实现排行榜的功能。

以现货增益排名举例:每个参赛团队都有一个唯一的ID和一个现货增益值(spotGain),可以设计以下结构:

Key: spot:rank:gain(排行榜的唯一标识)

Member: teamId(参赛团队ID)

Score: spotGain(现货增益值)

实现基本操作

(1)新增/更新参赛团队增益值

String rankKey = 'spot:rank:gain';

Long teamId = 2L;

double spotGain = 65.23;

// 更新现货增益值

redis.zadd(rankKey, spotGain, String.valueOf(teamId));

当团队的增益值发生变化时,可以调用zadd方法更新排行榜。如果团队已经存在于排行榜中,Redis会自动更新其分数。

(2)查询团队排名

// 获取参赛团队排名,Redis返回的排名是从0开始的

Long rank = redis.zrevrank(rankKey, String.valueOf(teamId));

if (rank != null) {

    log.info("参赛团队[{0}]的排名是 {2}", teamId, rank + 1);

} else {

    log.info("参赛团队[{0}]没有排名", teamId);

}

使用zrevrank方法可以获取参赛团队的排名,注意这里是倒序排列,即分数高的排在前面。

(3)获取排行榜前N名

// 获取前20名排名

Set<String> topRank = redis.zrevrange(rankKey, 0, 19);

通过zrevrange方法可以获取排行榜的前N名参赛团队及其对应的分数。

持久化与数据恢复

虽然Redis提供了高效的排行榜操作,但它毕竟是内存数据库,断电或服务器故障时可能导致数据丢失。为此,我们需要考虑数据的持久化问题。

1. 数据持久化

我们可以定期将Redis中的排行榜数据同步到MySQL中,确保数据的持久性。可以使用以下方式:

定时备份:通过定时任务将Redis中的排行榜数据导出,并写入MySQL。

更新时同步:每当参数团队增益值发生变化时,同时更新Redis和MySQL。

2. 数据恢复

当Redis服务器重启或数据丢失时,可以从MySQL中恢复排行榜数据。这样即使Redis中的数据丢失,我们也可以通过从MySQL恢复来保障排行榜的正常运行。

应对高并发与性能优化

在高并发场景下,我们需要考虑Redis的性能优化,以确保排行榜系统的稳定性和高效性。

使用集群: 当单台Redis服务器无法支撑高并发请求时,可以考虑使用Redis Cluster,将数据分布到多个节点中,提高系统的可扩展性。

限流与降级: 在高峰期

,可以对排行榜的查询和更新操作进行限流,避免Redis服务器被过度消耗。同时,也可以考虑在必要时进行功能降级,例如延迟更新排行榜或限制查询频率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛马程序员2025

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值