Java和Redis实现一个简单的热搜功能

news2024/11/15 14:43:32

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的set数据结构来存储用户的搜索历史。每个用户都有自己的历史记录集合,通过HISTORY_SET + ":" + userId来区分不同用户的搜索历史。
    • jedis.sadd方法用于添加新搜索词到历史记录集合中。
    • 删除操作没有直接在代码中体现,但可以通过jedis.srem方法从集合中移除某个元素来实现。
  2. 更新热搜列表
    • 我们使用Redis的有序集合(zset)来存储热搜数据。每个用户都有自己的有序集合,通过ZSET_PREFIX + userId来区分不同用户的热搜数据。
    • 每个搜索词都与一个得分相关联,该得分由函数getScore计算得出。新搜索词得分为1,旧搜索词得分为0。这个得分代表了搜索的频率和时间戳。
    • jedis.zadd方法用于向有序集合中添加新元素,并设置其得分。
  3. 获取平台上最热查询的十条数据
    • 我们使用jedis.zrevrangeWithScores方法获取有序集合中的前十个元素(得分最高的十个搜索词)。
    • 返回的结果是一个包含元素和得分的集合,我们通过流处理将其转换为列表。
  4. 不雅文字过滤
    • 这部分功能在代码中有直接实现,其原理是当用户输入搜索词时,系统会检查该词是否在预定义的BAD_WORDS列表中。
    • 如果在列表中,并且该词的搜索频率超过BAD_WORD_THRESHOLD,则认为这是一个不雅文字,将其替换为FILTERED_WORD
    • 注意:在实际应用中,可能需要更复杂的不雅文字过滤算法和策略,而不仅仅是基于频率的检查。

3. 注意事项

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1403799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4_机械臂运动学基础向量空间

在了解机械臂正解推导的过程中&#xff0c;几个问题一直困扰着我&#xff1a; 1、为什么3*3矩阵可以描述姿态&#xff1f;矩阵更进一步的意义是什么&#xff1f;姿态是否有其他的描述方式&#xff0c;如果有是什么&#xff1f; 2、机械臂法兰中心相对于基座的坐标&#xff0c;6…

开始学习vue2基础篇(初体验)

一、什么是VUE&#xff08;官网 &#xff1a;https://cn.vuejs.org/&#xff09; 官方给出的概念 &#xff1a;Vue (读音 /vju ː/ &#xff0c;类似于 view) 是一套用 于构建用户界面的前端框架 渐进式的 JavaScript 框架 二、VUE的特点 易用 &#xff1a;基础只需HTML、CSS、…

[小程序]页面事件

一、下拉刷新 1.开启和配置 小程序中开启下拉刷新的方式有两种&#xff1a; ①全局开启下来刷新 在app.json的window节点中&#xff0c;设置enablePullDownRefresh设为ture。 ②局部开启下来刷新 在页面对应的json文件的的window节点中&#xff0c;设置enablePullDownRefresh设…

yolov5 opencv dnn部署 github代码

yolov5 opencv dnn部署 github代码 源码地址实现推理源码中作者的yolov5s.onnx推理条件python部署(因为python比较简单就直接介绍了)c部署 参考链接 源码地址 yolov5官网还提供的dnn、tensorrt推理链接本人使用的opencv c github代码,代码作者非本人&#xff0c;也是上面作者推…

定向减免!函数计算让轻量 ETL 数据加工更简单,更省钱

作者&#xff1a;澈尔、墨飏 业内较为常见的高频短时 ETL 数据加工场景&#xff0c;即频率高时延短&#xff0c;一般均可归类为调用密集型场景。此场景有着高并发、海量调用的特性&#xff0c;往往会产生高额的计算费用&#xff0c;而业内推荐方案一般为攒批处理&#xff0c;业…

【EI会议征稿通知】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…

不建Vivado工程,也能看Device视图

不建Vivado工程&#xff0c;也能看Device视图 在FPGA设计与开发中&#xff0c;Device视图和Package视图发挥着重要的作用。 在Device视图下&#xff1a; 可以查看FPGA芯片可用资源 例如&#xff1a;LUT、FF、BRAM、DSP、URAM等的个数&#xff1b; 可以查看关键资源的分布情…

搭建redis服务器

memcached MongoDB Redis 先把数据存储在内存里,如何定期把内存里数据存储在硬盘,一个Key一个Values redis集群存储数据在内存里面 mysql集群存储数据在硬盘里 netstat -utnlp | grep redis-server 查看端口tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 1970/redis-server 1 …

性能优化(CPU优化技术)-NEON指令介绍

「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 NEON 指令相关的知识&#xff0c;首先通过讲解 arm 指令集的分类&#xff0c;NEON寄存器的类型&#xff0c;树立基本概念。然后进一步梳理了 NEON 汇编以及 intrinsics 指令的格式。最后结合指令的分类&#xff0c;使用例…

thinkadmin上传excel导入数据库

<div class="layui-form-item layui-inline"><button class="layui-btn layui-btn-primary">

正则化逻辑回归实战

一、题目 在正则化逻辑回归的练习中&#xff0c;我们将利用正则化的逻辑回归来预测来自制造工厂的微芯片是否通过了质量保证&#xff08;QA&#xff09;。在质量保证期间&#xff0c;每个微芯片都要经过各种测试&#xff0c;以确保其能够正常工作。假设您是该工厂的产品经理&am…

yolov8 opencv dnn部署 github代码

源码地址 本人使用的opencv c github代码,代码作者非本人 实现推理源码中作者的yolov8s.onnx 推理条件 windows 10 Visual Studio 2019 Nvidia GeForce GTX 1070 opencv4.7.0 (opencv4.5.5在别的地方看到不支持yolov8的推理&#xff0c;所以只使用opencv4.7.0) c部署 环境…

【MySQL】最左匹配原则

最左匹配原则 0x1 简单说下什么是最左匹配原则 顾名思义&#xff1a;最左优先&#xff0c;以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like&#xff09;就会停止匹配。 例如&#xff1a;b 2 如果建立(a&#xff0c;b&#xff09;顺序…

MySQL 索引优化:深入探索自适应哈希索引的奥秘

在数据库管理系统中&#xff0c;索引优化是提高查询性能的关键所在。MySQL 作为最流行的开源关系型数据库管理系统之一&#xff0c;提供了多种索引类型以满足不同查询场景的需求。其中&#xff0c;自适应哈希索引&#xff08;Adaptive Hash Index&#xff0c;AHI&#xff09;是…

Flink实战之DataStream API

接上文&#xff1a;Flink实战之运行架构 Flink的计算功能非常强大&#xff0c;提供的应用API也非常丰富。整体上来说&#xff0c;可以分为DataStreamAPI&#xff0c;DataSet API 和 Table与SQL API三大部分。 其中DataStream API是Flink中主要进行流计算的模块。 DateSet API是…

鸿蒙开发-UI-布局-栅格布局

鸿蒙开发-UI-布局 鸿蒙开发-UI-布局-线性布局 鸿蒙开发-UI-布局-层叠布局 鸿蒙开发-UI-布局-弹性布局 鸿蒙开发-UI-布局-相对布局 文章目录 前言 一、基本概念 二、格栅容器组件 1.栅格系统断点 2.布局的总列数 3.排列方向 4.子组件间距 三、格栅容器子组件 1.span 2.offset 3.…

【Unity小技巧】3D人物移动脚步和跳跃下落音效控制

文章目录 单脚步声多脚步声&#xff0c;跳跃落地音效播放不同材质的多脚步声完结 单脚步声 public AudioClip walkingSound; public AudioClip runningSound;//移动音效 public void MoveSound() {// 如果在地面上并且移动长度大于0.9if (isGround && moveDirection.s…

命令模式介绍

目录 一、命令模式介绍 1.1 命令模式定义 1.2 命令模式原理 1.2.1 命令模式类图 1.2.2 模式角色说明 二、命令模式的应用 2.1 需求说明 2.2 需求实现 2.2.1 抽象命令接口 2.2.2 订单类 2.2.3 厨师类 2.2.4 服务员类 2.2.5 具体命令类 2.2.6 测试类 三、命令模式总…

Ubuntu20.04输入法异常导致的黑屏:fcitx和ibus输入法的卸载与安装

Ubuntu20.04输入法异常导致的黑屏&#xff1a;fcitx和ibus输入法的卸载与安装_ubuntu卸载fcitx-CSDN博客 问题背景 系统&#xff1a;Ubuntu20.04 由于fcitx的不完整配置&#xff0c;导致fcitx输入法无法正常工作。决心卸载所有输入法&#xff0c;重新安装。但是由于在没有完整…

C#使用DateAndTime.DateDiff方法计算年龄

目录 一、计算年龄的方法 二、 DateAndTime类 1.定义 2.常用方法 3.DateDiff(DateInterval, DateTime, DateTime, FirstDayOfWeek, FirstWeekOfYear) 三、使用DateAndTime.DateDiff方法计算年龄 一、计算年龄的方法 使用DateDiff方法计算系统时间与员工生日之间相隔的年…