在社交平台上,热搜功能是一个非常重要的组成部分。它展示了当前最热门的话题,帮助用户迅速了解最受关注的事件。在微博等平台上,热搜榜单通常是实时变化的,可能会根据用户的互动数据(如搜索频次、点赞量、评论数等)动态调整。
Redis 是一个高性能的键值存储系统,通常用于缓存和实时数据存储,特别适用于实现类似热搜榜单这样的功能。本文将通过 Java 结合 Redis 实现一个简化版的微博热搜功能,展示如何使用 Redis 提供的高效数据结构,如 SortedSet
(有序集合)来维护热搜榜单。
热度的计算方式,不同的产品的热度计算方式不同:
例如:热度=评论数+转发数+点赞数
热度=搜索量+点击量
热搜排行榜可以分为:当前小时的热搜榜,当天的热搜榜,当月的热搜榜,当前周的热搜榜等等。
思路: 一般情况下,我们按照存储每个小时的小时榜,为最小单位。 同理,当天的热搜榜,就是这一天24小时的小时榜合并后的榜单。当月的热搜榜就是,这个月每天的热搜榜合并后的榜单。
1. Redis 数据结构介绍
在实现热搜功能时,Redis 提供的 SortedSet
(有序集合)是非常合适的。SortedSet
会根据分数(score)对成员(member)进行排序,适用于我们要根据热度(例如搜索频次、点赞数等)排序展示的场景。
- SortedSet(zset):是一个有序的集合,每个元素都有一个分数(score),Redis 会自动根据分数对元素进行排序。适合实现需要排序的场景,如热搜榜单、排行榜等。
2.当前小时的key的设定思路:
1.使用当前的时间戳来生成唯一的 key,通常使用 YYYY-MM-DD-HH
作为 key 的格式,这样每天每小时的数据都会有独立的 key。
例如:当前时间为 2024-12-10 07:25:21,那么对应的 key 为 2024-12-10-07
。
2.当前时间的毫秒的时间(T),当前小时唯一key=T/1000*60*60;
例如:当前时间为2024-12-10 16:10:40=1733818240000 ,那么对应的key就是:1733818240000 /1000*60*60=481,616
以上两种方式,都可以获取到当前时间的唯一的key。
3.代码实现
1. 引入依赖
在你的 pom.xml
中加入以下依赖来使用 Redis 和 Jedis:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
</dependencies>
主要需要实现的功能:热搜的小时榜,天榜,月榜的榜单的实现。
首先编写redis的基本操作的工具类:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Set;
public class RedisUtil {
private static JedisPool pool;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setTestOnBorrow(true);
pool = new JedisPool(config, "localhost", 6379); // Redis 地址
}
public static Jedis getJedis() {
return pool.getResource();
}
public static void close(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
public static void incrementSearchCount(String key,String topic) {
Jedis jedis = getJedis();
try {
// 使用 Redis 的 ZINCRBY 命令递增话题的热度
jedis.zincrby(key, 1, topic);
} finally {
close(jedis);
}
}
public static Set<String> getTopSearch(int topN) {
Jedis jedis = getJedis();
try {
// 使用 Redis 的 ZREVRANGE 命令获取热搜榜单
return jedis.zrevrange("hot_search", 0, topN - 1);
} finally {
close(jedis);
}
}
}
热搜的增加,每次都是单个增加的,此处不做解释。我们主要研究,如何查询,小时榜,天榜,月榜的数据。
具体代码如下:
@Autowired
private RedisTemplate redisTemplate;
/**
* 更新天的热搜数据
*/
public void updateDaySearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小时key的前缀
String hourPrefix = "search:hour";
//天的key
String dayPrefix = "search:day";
//计算最近24小时的key的集合
ArrayList<Object> dayKeys = new ArrayList<>();
//统计近24小时的key的集合
for (int i = 1; i < 23; i++) {
String key = hourPrefix+(hour-i);
dayKeys.add(key);
//设置当天的key 40天过期,防止资源的浪费
redisTemplate.expire(key,40, TimeUnit.DAYS);
}
//将近24小时的key的值进行合并
//(redisTemplate.opsForZSet()。unionAndStore(a,b,c);把a,b 两个zset合并储存到c集合中)
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,dayKeys,dayPrefix);
log.info("*********************进行天数据的更新************************");
}
public void updateMonthSearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小时key的前缀
String hourPrefix = "search:hour";
//天的key
String monthPrefix = "search:day";
//计算最近24小时的key的集合
ArrayList<Object> monthKeys = new ArrayList<>();
//统计近24小时的key的集合
for (int i = 1; i < 24*30-1; i++) {
String key = hourPrefix+(hour-i);
monthKeys.add(key);
}
//将近30天的key的值进行合并
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,monthKeys,monthPrefix);
log.info("***************进行月数据的更新*********************");
}
/**
* 更新最近7天的数据
*/
public void updateWeekSearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小时key的前缀
String hourPrefix = "search:hour";
//周的key
String weekPrefix = "search:week";
//计算最近24小时的key的集合
ArrayList<Object> weekKeys = new ArrayList<>();
//统计近24小时的key的集合
for (int i = 1; i < 24*7-1; i++) {
String key = hourPrefix+(hour-i);
weekKeys.add(key);
}
//将近30天的key的值进行合并
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,weekKeys,weekPrefix);
log.info("************进行周数据的更新******************");
}
/**
* 定时器每小时调用一次次方法进行,小时榜,天榜,周榜,月榜的更新
*/
public void updateAllSearch(){
//TODO 此方法建议使用定时框架,quartz ,xxl-job等进行定时调用
updateDaySearch();
updateWeekSearch();
updateMonthSearch();
}
4. 优化和扩展
- 热搜榜单过期策略:可以设置 Redis 中有序集合的过期时间,定期清理不再热门的关键词。
- 多维度排序:除了搜索次数外,可以根据时间窗口(例如过去一小时、一天内的搜索量)来做更加复杂的排序。
- 高并发优化:为了避免频繁的 Redis 操作影响性能,可以考虑将热搜数据批量更新或者使用消息队列来异步处理。
总结
通过以上的实现,我们展示了如何使用 Java 和 Redis 来实现一个简单的微博热搜功能。通过 Redis 的有序集合,我们能够高效地记录和排序搜索关键词的热度,并在用户请求时实时返回当前最热门的话题。
这种基于 Redis 的热搜功能不仅适用于微博,实际上可以广泛应用于各种需要统计热门话题或关键词的场景,如新闻网站、视频平台等。
希望本文对你理解 Redis 和 Java 的结合使用有所帮助,如果有任何问题或进一步的优化建议,欢迎在评论区讨论!
点点关注,点点赞呀,持续更新有用的知识............