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的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.hqwc.cn/news/423117.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt Linux安装qt5.9全过程

1 准备好安装包 qt安装包下载官网:https://download.qt.io/archive/qt 2 将安装包放到Linux环境下 我这里使用WinSCP将windows下的文件传输到Linux 3 ./qt-opensource-linux-x64-5.9.0.run 3-0 运行时目录的所有权错误wrong ownership on runtime directory 解决:切换roo…

K8S的helm

helm的作用 在没有helm之前&#xff0c;deploymen service ingress &#xff0c;helm的作用就是通过打包的方式&#xff0c;把deployment&#xff0c;service&#xff0c;ingress 这些打包在一块&#xff0c;一键式的部署服务&#xff0c;类似yum 官方提供的一个类似于安装仓库…

大数据开发之Scala

第 1 章&#xff1a;scala入门 1.1 概述 scala将面向对象和函数式编程结合成一种简洁的高级语言 特点 1、scala和java一样属于jvm语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且scala能够直接调用java的类库 2、scala支持两种编程范式面向对象和函数式…

Maven工程继承和聚合关系

1. Maven工程继承关系 1.1 继承概念 Maven 继承是指在 Maven 的项目中&#xff0c;让一个项目从另一个项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一配置信息&#xff0c;简化项目的管理和维护工作。 1.2 继承作用 在父工程中统一管理项目中的依赖信息。 …

初识 JVM

什么是JVM JVM 全称是 J ava V irtual M achine&#xff0c;中文译名 Java虚拟机 。 JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行 Java字节码文件 。 JVM的功能 Java语言如果不做任何优化&#xff0c;性能不如C、C等语言。 Java需要实时解释&…

什么是网络安全?网络安全概况

网络安全涉及保护我们的计算机网络、设备和数据免受未经授权的访问或破坏。 这个领域包括多种技术、过程和控制措施&#xff0c;旨在保护网络、设备和数据免受攻击、损害或未授权访问。网络安全涉及多个方面&#xff0c;包括但不限于信息安全、应用程序安全、操作系统安全等 …

仓储管理系统——软件工程报告(详细设计)④

详细设计 一、系统功能模块的划分 根据系统的功能性需求&#xff0c;本文将部队仓库管理系统分为以下六大模块&#xff1a;系统管理模 块、基础数据模块、出入库管理模块、库存管理模块、仓库信息管理模块、作业管理模 块&#xff0c;每个模块内部又分为很多小功能模块&#…

ADC的工作原理总结

ADC和DAC是联系连续和离散的重要桥梁&#xff0c;是信号采集和处理的重要环节。 这里我们先认识下ADC&#xff0c;聊一聊ADC的工作原理。 信号为何要相互转换&#xff1f; 模拟信号和数字信号的特点 自然界中大多数都是连续的模拟信号&#xff0c;模拟信号容易受到干扰&#…

华为AC+FIT AP组网配置

AC配置 vlan batch 100 to 101dhcp enableip pool apgateway-list 192.168.100.254 network 192.168.100.0 mask 255.255.255.0 interface Vlanif100ip address 192.168.100.254 255.255.255.0dhcp select globalinterface GigabitEthernet0/0/1port link-type trunkport trun…

Pixelmator Pro Mac版 v3.5 图像处理软件 兼容 M1/M2

在当今数字化时代&#xff0c;图像编辑软件成为了许多人必备的工具之一。无论您是摄影师、设计师还是普通用户&#xff0c;您都需要一款功能强大、易于使用的图像编辑软件来处理和优化您的照片和图像。而Pixelmator Pro for Mac正是满足这一需求的理想选择。 Pixelmator Pro f…

gradle构建spring-framework源码

5.3.22版本构建 通过启动的jvm参数配置代理下载 Could not download jruby-stdlib-9.2.20.1.jar (org.jruby:jruby-stdlib:9.2.20.1) Could not get resource https://repo.maven.apache.org/maven2/org/jruby/jruby-stdlib/9.2.20.1/jruby-stdlib-9.2.20.1.jar. Could not GE…