Java和Redis实现热搜功能

 更新时间:2024年01月28日 16:05:55   作者:孤蓬&听雨  
这篇文章主要介绍了Java和Redis实现热搜功能,在存储和传输用户搜索数据时,考虑到数据的机密性和隐私保护,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

1. 前言

我们有一个简单的需求:

  • 搜索栏展示当前登陆的个人用户的搜索历史记录,删除个人历史记录。
  • 用户在搜索栏输入某字符,则将该字符记录下来 以zset格式存储的redis中,记录该字符被搜索的个数以及当前的时间戳 (用了DFA算法)。
  • 每当用户查询了已在redis存在了的字符时,则直接累加个数, 用来获取平台上最热查询的十条数据。(可以自己写接口或者直接在redis中添加一些预备好的关键词)。
  • 做不雅文字的过滤功能。

2. 实现

2.1 引入依赖

<dependencies>  
    <dependency>  
        <groupId>redis.clients</groupId>  
        <artifactId>jedis</artifactId>  
        <version>3.7.0</version> <!-- 使用你需要的版本 -->  
    </dependency>  
</dependencies>

2.2 实现代码

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"; // 替换为需要过滤的关键词  
    private static final String FILTERED_WORD = "***"; // 替换为过滤后的关键词  
    private static final int BAD_WORD_THRESHOLD = 100; // 替换为过滤的阈值,超过则认为是不雅文字  
    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转为List,方便后续操作  
    public static void main(String[] args) {
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
        String userId = "user1"; // 当前登陆的个人用户ID,需要根据实际情况获取  
        String searchWord = "test"; // 需要搜索的字符  
        hotSearch(jedis, userId, searchWord);
    }
    public static void hotSearch(Jedis jedis, String userId, String searchWord) {
        // 获取当前用户的搜索历史记录  
        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中,记录该字符被搜索的个数以及当前的时间戳   
        jedis.zadd(ZSET_PREFIX + userId, getScore(searchWord), searchWord);
        System.out.println("Added " + searchWord + " to hot search with score " + getScore(searchWord));
        // 过滤不雅文字,如果是不雅文字则替换为***,并累加不雅文字的搜索次数  
        if (BAD_WORD_LIST.contains(searchWord)) {
            if (jedis.zscore(ZSET_PREFIX + userId, FILTERED_WORD) == null) { // 如果该词在zset中不存在,则加入并设置得分  
                jedis.zadd(ZSET_PREFIX + userId, BAD_WORD_THRESHOLD, FILTERED_WORD); // 设置得分为BAD_WORD_THRESHOLD,表示这是一个不雅文字  
                jedis.incrBy(HISTORY_SET + ":bad", 1); // 累加不雅文字的搜索次数,存储在bad历史的集合中,方便后续统计和过滤处理  
            } else { // 如果该词在zset中已存在,则只累加搜索次数,并更新得分(得分+1)  
                jedis.zincrby(ZSET_PREFIX + userId, 1, FILTERED_WORD); // 得分为当前得分+1,表示这是一个不雅文字的再次搜索  
                jedis.incrBy(HISTORY_SET + ":bad", 1); // 累加不雅文字的搜索次数,存储在bad历史的集合中,方便后续统计和过滤处理  
            }
            System.out.println("The word " + searchWord + " is filtered and replaced with " + FILTERED_WORD); // 输出过滤后的结果  
        } else { // 如果不是不雅文字,则正常加入热搜列表并设置得分  
            jedis.zadd(ZSET_PREFIX + userId, getScore(searchWord), searchWord); // 正常加入热搜列表并设置得分  
            System.out.println("Added normal word " + searchWord + " to hot search with score " + getScore(searchWord)); // 输出正常加入热搜列表的结果
        }
        // 获取平台上最热搜索的十条数据  
        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);
    }
    // 用于计算得分的方法,这里采用了最简单的得分方式,只考虑了搜索频率和时间戳,实际情况可能需要更复杂的算法 
    private static int getScore(String word) {
        return 1;
    }
}

2.3 实现原理

  1. 安全性
    • 确保Redis服务器的安全性。这包括使用强密码、配置防火墙规则、使用SSL连接等。不要将敏感数据暴露给不必要的用户或应用程序。
    • 在存储和传输用户搜索数据时,考虑到数据的机密性和隐私保护。根据当地的隐私法律和政策,可能需要采取额外的措施来保护用户数据。
  2. 性能监控和调优
    • 监控Redis的性能指标,如内存使用情况、连接数、查询速度等。根据实际负载情况,可能需要调整Redis的配置参数或增加硬件资源。
    • 定期检查代码的性能,确保在大量请求下能够保持稳定的性能。对于瓶颈部分,可能需要优化算法或调整数据结构。
  3. 异常处理
    • 添加适当的异常处理逻辑,以处理Redis连接失败、查询错误等情况。确保应用程序能够优雅地处理这些异常,并为用户提供适当的错误消息。
    • 对于可能出现的Redis故障或维护时段,考虑实现一种回退机制或通知系统,以便及时通知相关人员并采取措施。
  4. 数据一致性和备份
    • 确保Redis中的数据与应用程序中的其他数据源保持一致。在写入数据时,要确保幂等性以避免数据冲突。
    • 定期备份Redis中的数据,以防数据丢失。考虑使用快照或追加日志的方式来备份数据。
  5. 扩展性和高可用性
    • 如果应用程序需要处理大量的搜索请求,考虑使用Redis集群来分担负载和提高可用性。确保集群配置正确,并能够自动处理节点故障转移。
    • 在设计系统时,考虑到未来的扩展需求。使用可扩展的数据结构或算法,以便在需要时轻松地增加功能和优化性能。
  6. 日志和监控
    • 配置适当的日志记录系统,记录Redis的操作和关键事件。这有助于故障排查和性能分析。
    • 使用监控工具来实时跟踪Redis的性能指标和应用程序的健康状况。这样可以在问题发生时迅速采取行动。
  7. 测试和验证
    • 在将代码部署到生产环境之前,进行充分的测试和验证。确保代码的功能正确、性能良好,并且没有安全漏洞。
    • 考虑使用集成测试、单元测试和负载测试来评估代码的健壮性和稳定性。确保代码能够承受实际工作负载和各种边界条件。
  8. 代码维护和文档
    • 为代码添加适当的注释和文档,以帮助其他开发人员理解其工作原理和维护方式。这也有助于未来的代码审查和维护工作。
    • 保持代码的清洁和可维护性,遵循最佳实践和编码规范。定期重构代码以消除冗余和提高可读性。

到此这篇关于Java和Redis实现热搜功能的文章就介绍到这了,更多相关Redis热搜内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JDK 5 提供的注解:Target、Inherited和Documented的区别

    JDK 5 提供的注解:Target、Inherited和Documented的区别

    这篇文章主要介绍了JDK 5 提供的注解:Target、Inherited和Documented的区别,需要的朋友可以参考下
    2016-02-02
  • java递归处理单位人员组织机构树方式

    java递归处理单位人员组织机构树方式

    这篇文章主要介绍了java递归处理单位人员组织机构树方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • SpringBoot 项目中的图片处理策略之本地存储与路径映射

    SpringBoot 项目中的图片处理策略之本地存储与路径映射

    在SpringBoot项目中,静态资源存放在static目录下,使得前端可以通过URL来访问这些资源,我们就需要将文件系统的文件路径与URL建立一个映射关系,把文件系统中的文件当成我们的静态资源即可,本文给大家介绍SpringBoot本地存储与路径映射的相关知识,感兴趣的朋友一起看看吧
    2023-12-12
  • Spring的事件和监听器-同步与异步详解

    Spring的事件和监听器-同步与异步详解

    这篇文章主要介绍了Spring的事件和监听器-同步与异步详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java实现LRU缓存的实例详解

    Java实现LRU缓存的实例详解

    这篇文章主要介绍了Java实现LRU缓存的实例详解的相关资料,这里提供实例帮助大家理解掌握这部分内容,需要的朋友可以参考下
    2017-08-08
  • Java中SpringBoot的@Transactional原理

    Java中SpringBoot的@Transactional原理

    这篇文章主要介绍了Java中SpringBoot的@Transactional原理,面向元数据遍历已经成为越来越多开发者的偏好,因此原理从Springboot的EnableTransactionManagement注解说起,需要的朋友可以参考下
    2023-07-07
  • Hadoop之常用端口号解读

    Hadoop之常用端口号解读

    这篇文章主要介绍了Hadoop之常用端口号,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 浅谈Java向下转型的意义

    浅谈Java向下转型的意义

    这篇文章主要介绍了浅谈Java向下转型的意义,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • Spring中的@Resource源码解析

    Spring中的@Resource源码解析

    这篇文章主要介绍了Spring中的@Resource源码解析,这个注解加载的时候经过的类是CommonAnnotationBeanPostProcessor和Autowired一样,也是postProcessProperties()方法,需要的朋友可以参考下
    2023-09-09
  • Java 散列存储详解及简单示例

    Java 散列存储详解及简单示例

    这篇文章主要介绍了Java 散列存储详解及简单示例的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论