Java使用Redis實(shí)現(xiàn)微博熱搜功能
介紹
在社交平臺(tái)上,熱搜功能是一個(gè)非常重要的組成部分。它展示了當(dāng)前最熱門的話題,幫助用戶迅速了解最受關(guān)注的事件。在微博等平臺(tái)上,熱搜榜單通常是實(shí)時(shí)變化的,可能會(huì)根據(jù)用戶的互動(dòng)數(shù)據(jù)(如搜索頻次、點(diǎn)贊量、評(píng)論數(shù)等)動(dòng)態(tài)調(diào)整。
Redis 是一個(gè)高性能的鍵值存儲(chǔ)系統(tǒng),通常用于緩存和實(shí)時(shí)數(shù)據(jù)存儲(chǔ),特別適用于實(shí)現(xiàn)類似熱搜榜單這樣的功能。本文將通過 Java 結(jié)合 Redis 實(shí)現(xiàn)一個(gè)簡化版的微博熱搜功能,展示如何使用 Redis 提供的高效數(shù)據(jù)結(jié)構(gòu),如 SortedSet(有序集合)來維護(hù)熱搜榜單。

熱度的計(jì)算方式,不同的產(chǎn)品的熱度計(jì)算方式不同:
例如:
熱度=評(píng)論數(shù)+轉(zhuǎn)發(fā)數(shù)+點(diǎn)贊數(shù)
熱度=搜索量+點(diǎn)擊量
熱搜排行榜可以分為:當(dāng)前小時(shí)的熱搜榜,當(dāng)天的熱搜榜,當(dāng)月的熱搜榜,當(dāng)前周的熱搜榜等等。
思路: 一般情況下,我們按照存儲(chǔ)每個(gè)小時(shí)的小時(shí)榜,為最小單位。 同理,當(dāng)天的熱搜榜,就是這一天24小時(shí)的小時(shí)榜合并后的榜單。當(dāng)月的熱搜榜就是,這個(gè)月每天的熱搜榜合并后的榜單。
1. Redis 數(shù)據(jù)結(jié)構(gòu)介紹
在實(shí)現(xiàn)熱搜功能時(shí),Redis 提供的 SortedSet(有序集合)是非常合適的。SortedSet 會(huì)根據(jù)分?jǐn)?shù)(score)對(duì)成員(member)進(jìn)行排序,適用于我們要根據(jù)熱度(例如搜索頻次、點(diǎn)贊數(shù)等)排序展示的場景。
- SortedSet(zset):是一個(gè)有序的集合,每個(gè)元素都有一個(gè)分?jǐn)?shù)(score),Redis 會(huì)自動(dòng)根據(jù)分?jǐn)?shù)對(duì)元素進(jìn)行排序。適合實(shí)現(xiàn)需要排序的場景,如熱搜榜單、排行榜等。
2.當(dāng)前小時(shí)的key的設(shè)定思路:
1.使用當(dāng)前的時(shí)間戳來生成唯一的 key,通常使用 YYYY-MM-DD-HH 作為 key 的格式,這樣每天每小時(shí)的數(shù)據(jù)都會(huì)有獨(dú)立的 key。
例如:當(dāng)前時(shí)間為 2024-12-10 07:25:21,那么對(duì)應(yīng)的 key 為 2024-12-10-07。
2.當(dāng)前時(shí)間的毫秒的時(shí)間(T),當(dāng)前小時(shí)唯一key=T/1000*60*60;
例如:當(dāng)前時(shí)間為2024-12-10 16:10:40=1733818240000 ,那么對(duì)應(yīng)的key就是:1733818240000 /1000*60*60=481,616
以上兩種方式,都可以獲取到當(dāng)前時(shí)間的唯一的key。
3.代碼實(shí)現(xiàn)
1. 引入依賴
在你的 pom.xml 中加入以下依賴來使用 Redis 和 Jedis:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
</dependencies>主要需要實(shí)現(xiàn)的功能:熱搜的小時(shí)榜,天榜,月榜的榜單的實(shí)現(xiàn)。
首先編寫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);
}
}
}熱搜的增加,每次都是單個(gè)增加的,此處不做解釋。我們主要研究,如何查詢,小時(shí)榜,天榜,月榜的數(shù)據(jù)。
具體代碼如下:
@Autowired
private RedisTemplate redisTemplate;
/**
* 更新天的熱搜數(shù)據(jù)
*/
public void updateDaySearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小時(shí)key的前綴
String hourPrefix = "search:hour";
//天的key
String dayPrefix = "search:day";
//計(jì)算最近24小時(shí)的key的集合
ArrayList<Object> dayKeys = new ArrayList<>();
//統(tǒng)計(jì)近24小時(shí)的key的集合
for (int i = 1; i < 23; i++) {
String key = hourPrefix+(hour-i);
dayKeys.add(key);
//設(shè)置當(dāng)天的key 40天過期,防止資源的浪費(fèi)
redisTemplate.expire(key,40, TimeUnit.DAYS);
}
//將近24小時(shí)的key的值進(jìn)行合并
//(redisTemplate.opsForZSet()。unionAndStore(a,b,c);把a(bǔ),b 兩個(gè)zset合并儲(chǔ)存到c集合中)
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,dayKeys,dayPrefix);
log.info("*********************進(jìn)行天數(shù)據(jù)的更新************************");
}
public void updateMonthSearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小時(shí)key的前綴
String hourPrefix = "search:hour";
//天的key
String monthPrefix = "search:day";
//計(jì)算最近24小時(shí)的key的集合
ArrayList<Object> monthKeys = new ArrayList<>();
//統(tǒng)計(jì)近24小時(shí)的key的集合
for (int i = 1; i < 24*30-1; i++) {
String key = hourPrefix+(hour-i);
monthKeys.add(key);
}
//將近30天的key的值進(jìn)行合并
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,monthKeys,monthPrefix);
log.info("***************進(jìn)行月數(shù)據(jù)的更新*********************");
}
/**
* 更新最近7天的數(shù)據(jù)
*/
public void updateWeekSearch(){
long hour = System.currentTimeMillis()/(1000*60*60);
//小時(shí)key的前綴
String hourPrefix = "search:hour";
//周的key
String weekPrefix = "search:week";
//計(jì)算最近24小時(shí)的key的集合
ArrayList<Object> weekKeys = new ArrayList<>();
//統(tǒng)計(jì)近24小時(shí)的key的集合
for (int i = 1; i < 24*7-1; i++) {
String key = hourPrefix+(hour-i);
weekKeys.add(key);
}
//將近30天的key的值進(jìn)行合并
redisTemplate.opsForZSet().unionAndStore(hourPrefix+hour,weekKeys,weekPrefix);
log.info("************進(jìn)行周數(shù)據(jù)的更新******************");
}
/**
* 定時(shí)器每小時(shí)調(diào)用一次次方法進(jìn)行,小時(shí)榜,天榜,周榜,月榜的更新
*/
public void updateAllSearch(){
//TODO 此方法建議使用定時(shí)框架,quartz ,xxl-job等進(jìn)行定時(shí)調(diào)用
updateDaySearch();
updateWeekSearch();
updateMonthSearch();
}4. 優(yōu)化和擴(kuò)展
- 熱搜榜單過期策略:可以設(shè)置 Redis 中有序集合的過期時(shí)間,定期清理不再熱門的關(guān)鍵詞。
- 多維度排序:除了搜索次數(shù)外,可以根據(jù)時(shí)間窗口(例如過去一小時(shí)、一天內(nèi)的搜索量)來做更加復(fù)雜的排序。
- 高并發(fā)優(yōu)化:為了避免頻繁的 Redis 操作影響性能,可以考慮將熱搜數(shù)據(jù)批量更新或者使用消息隊(duì)列來異步處理。
總結(jié)
通過以上的實(shí)現(xiàn),我們展示了如何使用 Java 和 Redis 來實(shí)現(xiàn)一個(gè)簡單的微博熱搜功能。通過 Redis 的有序集合,我們能夠高效地記錄和排序搜索關(guān)鍵詞的熱度,并在用戶請(qǐng)求時(shí)實(shí)時(shí)返回當(dāng)前最熱門的話題。
這種基于 Redis 的熱搜功能不僅適用于微博,實(shí)際上可以廣泛應(yīng)用于各種需要統(tǒng)計(jì)熱門話題或關(guān)鍵詞的場景,如新聞網(wǎng)站、視頻平臺(tái)等。
以上就是Java使用Redis實(shí)現(xiàn)微博熱搜功能的詳細(xì)內(nèi)容,更多關(guān)于Java Redis微博熱搜的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
修改jar包package目錄結(jié)構(gòu)操作方法
這篇文章主要介紹了修改jar包package目錄結(jié)構(gòu)操作方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-07-07
Spring Boot使用過濾器和攔截器分別實(shí)現(xiàn)REST接口簡易安全認(rèn)證示例代碼詳解
這篇文章主要介紹了Spring Boot使用過濾器和攔截器分別實(shí)現(xiàn)REST接口簡易安全認(rèn)證示例代碼,通過開發(fā)實(shí)踐,理解過濾器和攔截器的工作原理,需要的朋友可以參考下2018-06-06
JAVA中SSM框架的搭建實(shí)現(xiàn)CRUD的方法
這篇文章主要介紹了JAVA中SSM框架的搭建實(shí)現(xiàn)CRUD的方法,小編把運(yùn)行代碼列了出來,供大家參考。2017-11-11
Java執(zhí)行SQL腳本文件到數(shù)據(jù)庫詳解
這篇文章主要為大家詳細(xì)介紹了Java執(zhí)行SQL腳本文件到數(shù)據(jù)庫的相關(guān)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
快速校驗(yàn)實(shí)體類時(shí),@Valid,@Validated,@NotNull注解無效的解決
這篇文章主要介紹了快速校驗(yàn)實(shí)體類時(shí),@Valid,@Validated,@NotNull注解無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringSecurity集成圖片驗(yàn)證碼的詳細(xì)過程
SpringSecurity是通過過濾器鏈來完成的,接下來的驗(yàn)證碼,可以嘗試創(chuàng)建一個(gè)過濾器放到Security的過濾器鏈中,在自定義的過濾器中比較驗(yàn)證碼,本文通過實(shí)例代碼介紹SpringSecurity集成圖片驗(yàn)證碼的詳細(xì)過程,感興趣的朋友一起看看吧2023-12-12

