Spring Boot 3 中利用 Redis 实现一亿用户实时积分排行全解析
bigegpt 2025-06-18 19:15 2 浏览
在当今互联网大厂后端开发的激烈竞争中,实现高效且实时的用户积分排行系统是众多产品脱颖而出的关键。对于拥有海量用户的应用,如游戏、社交平台等,实时展示用户积分排名,能极大地提升用户的参与感和竞争意识。今天,咱们就来深入探讨如何在 Spring Boot 3 中巧妙运用 Redis,实现一亿用户规模下的实时积分排行功能。
Redis—— 高性能数据存储神器
Redis,全称 Remote Dictionary Server,作为一款开源的内存数据结构存储数据库,自 2009 年诞生以来,凭借其卓越的性能,在互联网领域得到了广泛应用。它支持多种数据结构,像字符串、列表、集合、有序集合等。对于实时积分排行这一需求,Redis 的有序集合(Sorted Set)可谓是量身定制。
Redis 有序集合的独特魅力
高效排序:在有序集合中,每个元素都关联一个分数(Score),Redis 能够依据分数对元素进行快速排序。无论是插入新元素,还是删除现有元素,排序操作都能高效完成。这意味着当用户积分发生变动时,排行榜能迅速更新,确保用户看到的始终是最新排名。
超高读写速度:因为 Redis 将所有数据存储在内存中,避免了磁盘 I/O 的开销,所以读写速度极快,能轻松应对每秒几十万次的读写操作。对于拥有一亿用户的系统,如此高的读写性能是保证排行榜实时性的坚实基础。
丰富操作命令:Redis 提供了一系列针对有序集合的操作命令,例如,ZADD命令用于向有序集合中添加成员并设置分数,ZREVRANGE命令能按照分数从高到低获取指定范围内的成员,ZRANK命令则可获取某个成员的排名。这些丰富的命令,为实现复杂的排行榜功能提供了有力工具。
Spring Boot 3——Java 开发的得力助手
Spring Boot 自 2014 年发布以来,以其简化 Spring 应用初始化和搭建过程的优势,迅速成为 Java 开发领域的宠儿。Spring Boot 3 更是带来了诸多令人瞩目的新特性。
Spring Boot 3 的优势体现
对 Java 17 的强力支持:Java 17 作为长期支持(LTS)版本,具备许多新特性和性能改进。Spring Boot 3 对其的支持,让开发者能够充分利用这些优势,为应用注入更强大的活力。
基于 Spring Framework 6 的优化:这一优化使得 Spring Boot 3 在核心功能上有了显著提升,并且全面支持 Jakarta EE 9,为应用开发提供了更广阔的空间。
原生镜像支持:借助 GraalVM,Spring Boot 3 能够将 Java 应用编译成原生可执行文件。这不仅大幅减少了应用的启动时间,还降低了内存占用,特别适用于对性能要求苛刻的微服务和云原生应用场景,对于实现一亿用户实时积分排行的系统而言,无疑是如虎添翼。
Spring Boot 3 集成 Redis 实现实时积分排行
项目依赖配置
在 Spring Boot 3 项目中,要使用 Redis,首先得在pom.xml文件中添加相关依赖。通过引入
spring-boot-starter-data-redis依赖,项目就能轻松与 Redis 进行交互。例如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
同时,为了让项目能正常运行,别忘了配置 Redis 服务器的连接信息。在application.properties文件中添加如下内容:
spring.redis.host=你的Redis服务器地址
spring.redis.port=6379
spring.redis.password=你的Redis密码(若有)
代码实现
创建 Redis 操作服务类:在项目中创建一个专门用于操作 Redis 有序集合的服务类,比如LeaderboardService。在这个类中,利用 Spring 提供的RedisTemplate来操作有序集合。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class LeaderboardService {
private static final String LEADERBOARD_KEY = "userScoreLeaderboard";
@Autowired
private RedisTemplate<String, Double> redisTemplate;
// 更新用户积分
public void updateScore(String userId, double score) {
redisTemplate.opsForZSet().add(LEADERBOARD_KEY, userId, score);
}
// 获取排行榜前N名用户
public Set<String> getTopUsers(int topN) {
return redisTemplate.opsForZSet().reverseRange(LEADERBOARD_KEY, 0, topN - 1);
}
// 获取某个用户的排名
public Long getUserRank(String userId) {
return redisTemplate.opsForZSet().reverseRank(LEADERBOARD_KEY, userId);
}
}
创建控制器类:接着,创建一个控制器类LeaderboardController,用于接收前端传来的请求,并调用LeaderboardService中的方法进行处理。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
@RestController
@RequestMapping("/api/leaderboard")
public class LeaderboardController {
@Autowired
private LeaderboardService leaderboardService;
// 更新用户积分
@PostMapping("/updateScore")
public String updateScore(@RequestParam String userId, @RequestParam double score) {
leaderboardService.updateScore(userId, score);
return "User score updated successfully.";
}
// 获取排行榜前N名用户
@GetMapping("/top")
public Set<String> getTopUsers(@RequestParam int topN) {
return leaderboardService.getTopUsers(topN);
}
// 获取某个用户的排名
@GetMapping("/rank/{userId}")
public Long getUserRank(@PathVariable String userId) {
return leaderboardService.getUserRank(userId);
}
}
代码解析
LeaderboardService类:在updateScore方法中,通过redisTemplate.opsForZSet().add(LEADERBOARD_KEY, userId, score),将用户 ID 和对应的积分添加到 Redis 的有序集合中。在getTopUsers方法里,利用redisTemplate.opsForZSet().reverseRange(LEADERBOARD_KEY, 0, topN - 1),按照积分从高到低的顺序,获取排行榜前 N 名用户。而在getUserRank方法中,借助redisTemplate.opsForZSet().reverseRank(LEADERBOARD_KEY, userId),获取指定用户在排行榜中的排名。
LeaderboardController类:在updateScore方法里,接收前端传来的用户 ID 和积分,调用LeaderboardService的updateScore方法进行积分更新,并返回更新成功的提示信息。getTopUsers方法接收前端指定的 N 值,调用LeaderboardService的getTopUsers方法,返回排行榜前 N 名用户。getUserRank方法接收前端传来的用户 ID,调用LeaderboardService的getUserRank方法,返回该用户的排名。
面对一亿用户规模的优化策略
数据分片存储
考虑到一亿用户的数据量巨大,将所有用户的积分数据存储在一个 Redis 实例中,可能会导致内存压力过大,甚至出现性能瓶颈。此时,数据分片存储是个不错的解决方案。可以依据用户 ID 的哈希值,将用户数据分散存储到多个 Redis 实例中。例如,通过如下代码实现简单的数据分片:
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class ShardedLeaderboardService {
private final ShardedJedisPool shardedJedisPool;
private static final String LEADERBOARD_PREFIX = "leaderboard_";
public ShardedLeaderboardService(List<JedisShardInfo> shardInfos) {
this.shardedJedisPool = new ShardedJedisPool(shardInfos);
}
// 获取分片索引
private int getShardIndex(String userId) {
return Math.abs(userId.hashCode() % shardedJedisPool.getShards().size());
}
// 更新用户积分
public void updateScore(String userId, double score) {
try (ShardedJedis shardedJedis = shardedJedisPool.getResource()) {
int shardIndex = getShardIndex(userId);
Jedis shard = shardedJedis.getShard(LEADERBOARD_PREFIX + shardIndex);
shard.zadd(LEADERBOARD_PREFIX + shardIndex, score, userId);
}
}
// 合并分片排行榜
public void mergeGlobalLeaderboard() {
Map<String, Double> globalScores = new HashMap<>();
try (ShardedJedis shardedJedis = shardedJedisPool.getResource()) {
for (int i = 0; i < shardedJedisPool.getShards().size(); i++) {
Jedis shard = shardedJedis.getShard(LEADERBOARD_PREFIX + i);
Map<String, Double> scores = shard.zrangeWithScores(LEADERBOARD_PREFIX + i, 0, -1);
scores.forEach((userId, score) -> globalScores.merge(userId, score, Double::max));
}
Jedis globalRedis = shardedJedis.getShard(LEADERBOARD_PREFIX + 0);
globalScores.forEach((userId, score) -> globalRedis.zadd("global_leaderboard", score, userId));
}
}
}
缓存热门数据
对于频繁查询的热门数据,如排行榜的前 100 名,为了减少对 Redis 的频繁访问,可以利用本地缓存(如 Guava Cache)进行缓存。例如:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Service
public class CachingLeaderboardService {
private final LoadingCache<Integer, Set<String>> topUsersCache;
@Autowired
private LeaderboardService leaderboardService;
public CachingLeaderboardService() {
this.topUsersCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(100)
.build(new CacheLoader<Integer, Set<String>>() {
@Override
public Set<String> load(Integer topN) throws Exception {
return leaderboardService.getTopUsers(topN);
}
});
}
public Set<String> getTopUsers(int topN) {
try {
return topUsersCache.get(topN);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
异步更新机制
在处理用户积分更新时,采用异步更新机制,将积分更新操作放入消息队列(如 Kafka 或 RabbitMQ)中。这样,主业务线程无需等待积分更新完成,就能快速响应用户请求,提升系统的整体响应速度。例如,在 Spring Boot 项目中,可以借助 Spring Integration 来实现消息队列与积分更新服务的集成。首先,添加 Spring Integration 和消息队列相关的依赖,然后配置消息通道和处理器。
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
</dependency>
在配置文件中:
<int-amqp:outbound-channel-adapter
channel="scoreUpdateChannel"
amqp-template="amqpTemplate"
exchange-name="scoreUpdateExchange"
routing-key="scoreUpdateRoutingKey"/>
<int:service-activator input-channel="scoreUpdateChannel" ref="scoreUpdateService" method="updateScore"/>
监控与报警
部署专业的监控工具,如 Prometheus 和 Grafana,对系统的性能指标,如 Redis 的内存使用情况、读写操作频率、系统响应时间等进行实时监控。一旦指标超出预设的阈值,立即通过邮件、短信等方式发出报警,以便及时进行处理,保障系统的稳定运行。例如,在 Spring Boot 项目中,可以通过添加 Micrometer 相关依赖,将应用的指标数据暴露给 Prometheus。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
然后,在application.properties文件中配置相关属性,启动应用后,Prometheus 就能采集到应用的各项指标数据,并通过 Grafana 进行可视化展示。
总结
通过在 Spring Boot 3 项目中集成 Redis,利用 Redis 有序集合的特性,我们能够高效地实现一亿用户规模下的实时积分排行功能。同时,结合数据分片存储、缓存热门数据、异步更新机制以及监控报警等优化策略,能够确保系统在高并发、大数据量的场景下稳定运行,为用户提供流畅、实时的排行榜体验。希望本文的内容能为各位互联网大厂的后端开发同仁们在实际项目中提供有益的参考和帮助。在技术的道路上,让我们不断探索,共同进步,打造出更强大、更优秀的互联网应用!
相关推荐
- 悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)
-
新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...
- 高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源
-
凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...
- 微服务架构实战:商家管理后台与sso设计,SSO客户端设计
-
SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...
- 还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑
-
在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...
- Seata源码—6.Seata AT模式的数据源代理二
-
大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...
- 30分钟了解K8S(30分钟了解微积分)
-
微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
-
一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...
- 一招解决所有依赖冲突(克服依赖)
-
背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...
- 你读过Mybatis的源码?说说它用到了几种设计模式
-
学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...
- golang对接阿里云私有Bucket上传图片、授权访问图片
-
1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...
- spring中的资源的加载(spring加载原理)
-
最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...
- Android资源使用(android资源文件)
-
Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...
- 如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)
-
深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...
- @Autowired与@Resource原理知识点详解
-
springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...
- java的redis连接工具篇(java redis client)
-
在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)