Java和Redis實(shí)現(xiàn)熱搜功能
1. 前言
我們有一個(gè)簡單的需求:
- 搜索欄展示當(dāng)前登陸的個(gè)人用戶的搜索歷史記錄,刪除個(gè)人歷史記錄。
- 用戶在搜索欄輸入某字符,則將該字符記錄下來 以zset格式存儲(chǔ)的redis中,記錄該字符被搜索的個(gè)數(shù)以及當(dāng)前的時(shí)間戳 (用了DFA算法)。
- 每當(dāng)用戶查詢了已在redis存在了的字符時(shí),則直接累加個(gè)數(shù), 用來獲取平臺(tái)上最熱查詢的十條數(shù)據(jù)。(可以自己寫接口或者直接在redis中添加一些預(yù)備好的關(guān)鍵詞)。
- 做不雅文字的過濾功能。

2. 實(shí)現(xiàn)
2.1 引入依賴
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version> <!-- 使用你需要的版本 -->
</dependency>
</dependencies>2.2 實(shí)現(xiàn)代碼
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class HotSearch {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
private static final String HISTORY_SET = "history";
private static final String ZSET_PREFIX = "zset:";
private static final int TOP_TEN = 10;
private static final String BAD_WORDS = "bad"; // 替換為需要過濾的關(guān)鍵詞
private static final String FILTERED_WORD = "***"; // 替換為過濾后的關(guān)鍵詞
private static final int BAD_WORD_THRESHOLD = 100; // 替換為過濾的閾值,超過則認(rèn)為是不雅文字
private static final List<String> BAD_WORD_LIST = IntStream.range(0, BAD_WORDS.length()).mapToObj(i -> BAD_WORDS.substring(i, i + 1)).collect(Collectors.toList()); // 將BAD_WORDS轉(zhuǎn)為List,方便后續(xù)操作
public static void main(String[] args) {
Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
String userId = "user1"; // 當(dāng)前登陸的個(gè)人用戶ID,需要根據(jù)實(shí)際情況獲取
String searchWord = "test"; // 需要搜索的字符
hotSearch(jedis, userId, searchWord);
}
public static void hotSearch(Jedis jedis, String userId, String searchWord) {
// 獲取當(dāng)前用戶的搜索歷史記錄
Set<String> history = jedis.smembers(HISTORY_SET + ":" + userId);
if (history == null) history = new HashSet<>();
history.add(searchWord); // 將新搜索詞加入歷史記錄
jedis.sadd(HISTORY_SET + ":" + userId, history); // 將歷史記錄存入redis中
history.remove(searchWord); // 去掉新搜索詞,只保留舊的歷史記錄
// 將搜索詞加入zset中,記錄該字符被搜索的個(gè)數(shù)以及當(dāng)前的時(shí)間戳
jedis.zadd(ZSET_PREFIX + userId, getScore(searchWord), searchWord);
System.out.println("Added " + searchWord + " to hot search with score " + getScore(searchWord));
// 過濾不雅文字,如果是不雅文字則替換為***,并累加不雅文字的搜索次數(shù)
if (BAD_WORD_LIST.contains(searchWord)) {
if (jedis.zscore(ZSET_PREFIX + userId, FILTERED_WORD) == null) { // 如果該詞在zset中不存在,則加入并設(shè)置得分
jedis.zadd(ZSET_PREFIX + userId, BAD_WORD_THRESHOLD, FILTERED_WORD); // 設(shè)置得分為BAD_WORD_THRESHOLD,表示這是一個(gè)不雅文字
jedis.incrBy(HISTORY_SET + ":bad", 1); // 累加不雅文字的搜索次數(shù),存儲(chǔ)在bad歷史的集合中,方便后續(xù)統(tǒng)計(jì)和過濾處理
} else { // 如果該詞在zset中已存在,則只累加搜索次數(shù),并更新得分(得分+1)
jedis.zincrby(ZSET_PREFIX + userId, 1, FILTERED_WORD); // 得分為當(dāng)前得分+1,表示這是一個(gè)不雅文字的再次搜索
jedis.incrBy(HISTORY_SET + ":bad", 1); // 累加不雅文字的搜索次數(shù),存儲(chǔ)在bad歷史的集合中,方便后續(xù)統(tǒng)計(jì)和過濾處理
}
System.out.println("The word " + searchWord + " is filtered and replaced with " + FILTERED_WORD); // 輸出過濾后的結(jié)果
} else { // 如果不是不雅文字,則正常加入熱搜列表并設(shè)置得分
jedis.zadd(ZSET_PREFIX + userId, getScore(searchWord), searchWord); // 正常加入熱搜列表并設(shè)置得分
System.out.println("Added normal word " + searchWord + " to hot search with score " + getScore(searchWord)); // 輸出正常加入熱搜列表的結(jié)果
}
// 獲取平臺(tái)上最熱搜索的十條數(shù)據(jù)
Set<Tuple> hotData = jedis.zrevrangeWithScores(ZSET_PREFIX + userId, 0, TOP_TEN - 1);
List<String> hotWords = hotData.stream().map(Tuple::getElement).collect(Collectors.toList());
List<Integer> hotScores = hotData.stream().map(Tuple::getScore).collect(Collectors.toList());
System.out.println("Top " + TOP_TEN + " hot searches are: " + hotWords + " with scores: " + hotScores);
}
// 用于計(jì)算得分的方法,這里采用了最簡單的得分方式,只考慮了搜索頻率和時(shí)間戳,實(shí)際情況可能需要更復(fù)雜的算法
private static int getScore(String word) {
return 1;
}
}2.3 實(shí)現(xiàn)原理
- 安全性:
- 確保Redis服務(wù)器的安全性。這包括使用強(qiáng)密碼、配置防火墻規(guī)則、使用SSL連接等。不要將敏感數(shù)據(jù)暴露給不必要的用戶或應(yīng)用程序。
- 在存儲(chǔ)和傳輸用戶搜索數(shù)據(jù)時(shí),考慮到數(shù)據(jù)的機(jī)密性和隱私保護(hù)。根據(jù)當(dāng)?shù)氐碾[私法律和政策,可能需要采取額外的措施來保護(hù)用戶數(shù)據(jù)。
- 性能監(jiān)控和調(diào)優(yōu):
- 監(jiān)控Redis的性能指標(biāo),如內(nèi)存使用情況、連接數(shù)、查詢速度等。根據(jù)實(shí)際負(fù)載情況,可能需要調(diào)整Redis的配置參數(shù)或增加硬件資源。
- 定期檢查代碼的性能,確保在大量請(qǐng)求下能夠保持穩(wěn)定的性能。對(duì)于瓶頸部分,可能需要優(yōu)化算法或調(diào)整數(shù)據(jù)結(jié)構(gòu)。
- 異常處理:
- 添加適當(dāng)?shù)漠惓L幚磉壿?,以處理Redis連接失敗、查詢錯(cuò)誤等情況。確保應(yīng)用程序能夠優(yōu)雅地處理這些異常,并為用戶提供適當(dāng)?shù)腻e(cuò)誤消息。
- 對(duì)于可能出現(xiàn)的Redis故障或維護(hù)時(shí)段,考慮實(shí)現(xiàn)一種回退機(jī)制或通知系統(tǒng),以便及時(shí)通知相關(guān)人員并采取措施。
- 數(shù)據(jù)一致性和備份:
- 確保Redis中的數(shù)據(jù)與應(yīng)用程序中的其他數(shù)據(jù)源保持一致。在寫入數(shù)據(jù)時(shí),要確保冪等性以避免數(shù)據(jù)沖突。
- 定期備份Redis中的數(shù)據(jù),以防數(shù)據(jù)丟失??紤]使用快照或追加日志的方式來備份數(shù)據(jù)。
- 擴(kuò)展性和高可用性:
- 如果應(yīng)用程序需要處理大量的搜索請(qǐng)求,考慮使用Redis集群來分擔(dān)負(fù)載和提高可用性。確保集群配置正確,并能夠自動(dòng)處理節(jié)點(diǎn)故障轉(zhuǎn)移。
- 在設(shè)計(jì)系統(tǒng)時(shí),考慮到未來的擴(kuò)展需求。使用可擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)或算法,以便在需要時(shí)輕松地增加功能和優(yōu)化性能。
- 日志和監(jiān)控:
- 配置適當(dāng)?shù)娜罩居涗浵到y(tǒng),記錄Redis的操作和關(guān)鍵事件。這有助于故障排查和性能分析。
- 使用監(jiān)控工具來實(shí)時(shí)跟蹤Redis的性能指標(biāo)和應(yīng)用程序的健康狀況。這樣可以在問題發(fā)生時(shí)迅速采取行動(dòng)。
- 測試和驗(yàn)證:
- 在將代碼部署到生產(chǎn)環(huán)境之前,進(jìn)行充分的測試和驗(yàn)證。確保代碼的功能正確、性能良好,并且沒有安全漏洞。
- 考慮使用集成測試、單元測試和負(fù)載測試來評(píng)估代碼的健壯性和穩(wěn)定性。確保代碼能夠承受實(shí)際工作負(fù)載和各種邊界條件。
- 代碼維護(hù)和文檔:
- 為代碼添加適當(dāng)?shù)淖⑨尯臀臋n,以幫助其他開發(fā)人員理解其工作原理和維護(hù)方式。這也有助于未來的代碼審查和維護(hù)工作。
- 保持代碼的清潔和可維護(hù)性,遵循最佳實(shí)踐和編碼規(guī)范。定期重構(gòu)代碼以消除冗余和提高可讀性。
到此這篇關(guān)于Java和Redis實(shí)現(xiàn)熱搜功能的文章就介紹到這了,更多相關(guān)Redis熱搜內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?CompletableFuture實(shí)現(xiàn)多線程異步編排
這篇文章主要為大家介紹了Java?CompletableFuture實(shí)現(xiàn)多線程異步編排,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
詳解Java高并發(fā)編程之AtomicReference
此篇文章主要介紹了AtomicReference的出現(xiàn)背景,AtomicReference的使用場景,以及介紹了AtomicReference的源碼,重點(diǎn)方法的源碼分析2021-06-06
Java實(shí)現(xiàn)年獸大作戰(zhàn)游戲詳解
春節(jié)要到了,看慣了前端各種小游戲,確實(shí)做得很好,很精致。本文將為大家介紹一款java版本的年獸大作戰(zhàn)游戲,感興趣的小伙伴可以試一試2022-01-01
java8如何根據(jù)某一屬性條件快速篩選list中的集合
這篇文章主要介紹了java8如何根據(jù)某一屬性條件快速篩選list中的集合,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
如何使用spring ResponseEntity處理http響應(yīng)
這篇文章主要介紹了如何使用spring ResponseEntity處理http響應(yīng)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java靜態(tài)代理和動(dòng)態(tài)代理詳解
這篇文章主要介紹了Java靜態(tài)代理和動(dòng)態(tài)代理,本文通過代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-11-11
java中JSONArray互相轉(zhuǎn)換List的實(shí)現(xiàn)
本文主要介紹了java中JSONArray互相轉(zhuǎn)換List的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

