Java使用Redis實(shí)現(xiàn)微博熱搜功能
介紹
在社交平臺上,熱搜功能是一個(gè)非常重要的組成部分。它展示了當(dāng)前最熱門的話題,幫助用戶迅速了解最受關(guān)注的事件。在微博等平臺上,熱搜榜單通常是實(shí)時(shí)變化的,可能會根據(jù)用戶的互動數(shù)據(jù)(如搜索頻次、點(diǎn)贊量、評論數(shù)等)動態(tài)調(diào)整。
Redis 是一個(gè)高性能的鍵值存儲系統(tǒng),通常用于緩存和實(shí)時(shí)數(shù)據(jù)存儲,特別適用于實(shí)現(xiàn)類似熱搜榜單這樣的功能。本文將通過 Java 結(jié)合 Redis 實(shí)現(xiàn)一個(gè)簡化版的微博熱搜功能,展示如何使用 Redis 提供的高效數(shù)據(jù)結(jié)構(gòu),如 SortedSet(有序集合)來維護(hù)熱搜榜單。
熱度的計(jì)算方式,不同的產(chǎn)品的熱度計(jì)算方式不同:
例如:
熱度=評論數(shù)+轉(zhuǎn)發(fā)數(shù)+點(diǎn)贊數(shù)
熱度=搜索量+點(diǎn)擊量
熱搜排行榜可以分為:當(dāng)前小時(shí)的熱搜榜,當(dāng)天的熱搜榜,當(dāng)月的熱搜榜,當(dāng)前周的熱搜榜等等。
思路: 一般情況下,我們按照存儲每個(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
會根據(jù)分?jǐn)?shù)(score)對成員(member)進(jìn)行排序,適用于我們要根據(jù)熱度(例如搜索頻次、點(diǎn)贊數(shù)等)排序展示的場景。
- SortedSet(zset):是一個(gè)有序的集合,每個(gè)元素都有一個(gè)分?jǐn)?shù)(score),Redis 會自動根據(jù)分?jǐn)?shù)對元素進(jìn)行排序。適合實(shí)現(xiàn)需要排序的場景,如熱搜榜單、排行榜等。
2.當(dāng)前小時(shí)的key的設(shè)定思路:
1.使用當(dāng)前的時(shí)間戳來生成唯一的 key,通常使用 YYYY-MM-DD-HH 作為 key 的格式,這樣每天每小時(shí)的數(shù)據(jù)都會有獨(dú)立的 key。
例如:當(dāng)前時(shí)間為 2024-12-10 07:25:21,那么對應(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 ,那么對應(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合并儲存到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)鍵詞的熱度,并在用戶請求時(shí)實(shí)時(shí)返回當(dāng)前最熱門的話題。
這種基于 Redis 的熱搜功能不僅適用于微博,實(shí)際上可以廣泛應(yīng)用于各種需要統(tǒng)計(jì)熱門話題或關(guān)鍵詞的場景,如新聞網(wǎng)站、視頻平臺等。
以上就是Java使用Redis實(shí)現(xiàn)微博熱搜功能的詳細(xì)內(nèi)容,更多關(guān)于Java Redis微博熱搜的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能,涉及Java文件讀取及base64 轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2018-05-05SpringBoot使用FreeMarker模板發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用FreeMarker模板發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04Java線程池并發(fā)執(zhí)行多個(gè)任務(wù)方式
這篇文章主要介紹了Java線程池并發(fā)執(zhí)行多個(gè)任務(wù)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08MyBatis存儲過程、MyBatis分頁、MyBatis一對多增刪改查操作
本文通過一段代碼給大家介紹了MyBatis存儲過程、MyBatis分頁、MyBatis一對多增刪改查操作,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-11-11idea中創(chuàng)建jsp項(xiàng)目的詳細(xì)實(shí)戰(zhàn)步驟
才學(xué)javaWeb,以防自己忘記創(chuàng)建項(xiàng)目的過程,所以淺淺的記錄一下吧,下面這篇文章主要給大家介紹了關(guān)于idea中創(chuàng)建jsp項(xiàng)目的詳細(xì)步驟,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09

spring boot項(xiàng)目中MongoDB的使用方法