搜索记录及使用缓存的设计思路

目录

背景:

思路:

问题

实现


背景:

        最近业务写到检索这块的内容,设计到搜索记录的增删操作。考虑到频繁写库操作所以使用缓存技术 redis 和 cacha。

思路:

        查询:先查内存查缓存在查询数据库,查询数据库先更新内存在更新缓存    

        插入:因为搜索历史不同于文章统计功能改变其根据id 修改uv pv 值即可,也考虑到检索是否正常执行完成,可以保证其搜索内容的唯一性(之前就是搜索一次添加一行数据),修改其检索时间查询数据库根据检索时间倒序也无需在数据库层面去重操作。搜索内容在历史已经存在,更新内存缓存的检索时间,如果不存在,添加后同步到内存在缓存。

        删除:删除数据库的搜索内容删除刷新内存缓存,或者数据库有状态标识,但重新搜索同样内容需要考虑是这条数据是做修改操作还是另外新增。第一种方式简单方便高效数据库也干净

        修改:无修改操作

问题

        在实现的过程中发现历史搜索并不单纯只包含搜索内容,搜索的时间命中的数据方便,可用于后期实现搜索相关的计算和统计。所以搜索插入的时候不能靠搜索内容省去去重的操作,所以每次搜索的时候相关的信息需要全部入缓存然后再同步库。

实现

       搜索时先将搜索内容入库获获取主键id,方便于后期可以将数据不显示

    @ApiOperation("模糊搜索")@PostMapping("/dim")private AjaxResult dimSearch(@RequestBody @Validated DimSearchParam param) {SearchTypeEnum searchType = SearchTypeEnum.getSearchType(param.getType());// 1.保存搜索记录SearchHistory searchHistory = searchHistoryService.saveSearchInfo(param.getSearchContent(), searchType.name(), SecurityUtils.getUserId());// 2.添加搜素记录缓存LatelySearchRecordVo latelySearchRecordVo = new LatelySearchRecordVo(param.getSearchContent(), searchHistory.getId());searchCacheService.addSearchRecordLocalCache(SecurityUtils.getUserId(), searchType, latelySearchRecordVo);// 3.组装参数SearchDim search = (SearchDim) searchBeanContainer.getSearchBean(searchType.getBeanName());param.setSearchType(searchType);param.setId(searchHistory.getId());SearchContext<EsPageInfo, DimSearchParam> searchContext = new SearchContext<>(search);// 4.执行搜索EsPageInfo esPageInfo= searchContext.execute(param);return AjaxResult.success(esPageInfo);}

        本地缓存使用了caffeine,高可用,且对数据删除淘汰机制都提供了方法

  private final Cache<String, Set<LatelySearchRecordVo>> searchContentCache = Caffeine.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS).maximumSize(1000).build();

        添加本地缓存和redis缓存 (搜索记录需要去重,所以删除了之前搜素内容相同的)

    @PostMapping("/lately/search/record")@ApiOperation(value = "最近搜索记录")private AjaxResult latelySearchRecord(@RequestBody LatelySearchRecordParam param){SearchTypeEnum searchType = verifySearchType(param.getType());// 1. 获取缓存Long userId = SecurityUtils.getUserId();Set<LatelySearchRecordVo> cacheVo = searchCacheService.getSearchRecordCacheByLocal(userId, searchType, param.getPage(),  param.getSize());if (StringUtils.isEmpty(cacheVo)){startPage();return success(searchHistoryService.latelySearchRecord(searchType, userId));}return success(cacheVo);}
 /*** 添加搜索记录缓存*/public void addSearchRecordLocalCache(Long userId, SearchTypeEnum searchType, LatelySearchRecordVo searchHistory) {String localCacheKey = LocalCacheKey.getSearchContentKey(userId, searchType);Set<LatelySearchRecordVo> localCacheSet = searchContentCache.getIfPresent(localCacheKey);if (StringUtils.isEmpty(localCacheSet)) {localCacheSet = new LinkedHashSet<>();}// 1.添加内存localCacheSet.removeIf(l -> l.getSearchContent().equals(searchHistory.getSearchContent()));localCacheSet.add(searchHistory);searchContentCache.put(localCacheKey, localCacheSet);addSearchRecordLocalRedis(userId, searchType, searchHistory);}/*** 添加搜索记录缓存*/public void addSearchRecordLocalRedis(Long userId, SearchTypeEnum searchType, LatelySearchRecordVo searchHistory) {String redisCacheKey = RedisCacheKey.getSearchContentKey(userId, searchType);ZSetOperations<String, LatelySearchRecordVo> redisZet = redisTemplate.opsForZSet();// 2.缓存存在的清空下需要去重相同搜素内容if (redisTemplate.hasKey(redisCacheKey)){long length = redisZet.size(redisCacheKey) - 1;Set<LatelySearchRecordVo> redisCacheSet = redisZet.range(redisCacheKey, 0, length);Set<LatelySearchRecordVo> delCache= redisCacheSet.stream().filter(l -> l.getSearchContent().equals(searchHistory.getSearchContent())).collect(Collectors.toSet());if (!StringUtils.isEmpty(delCache)){redisZet.remove(redisCacheKey, delCache.toArray());}}// 3.添加缓存redisZet.add(redisCacheKey, searchHistory, System.currentTimeMillis());redisTemplate.expire(redisCacheKey, 30, TimeUnit.MINUTES);}

        在查询搜索记录时优先从缓存获取

  /*** 从本地缓存获取搜索记录*/public Set<LatelySearchRecordVo> getSearchRecordCacheByRedis(Long userId, SearchTypeEnum searchType, int pageNum, int pageSize) {String redisCacheKey = RedisCacheKey.getSearchContentKey(userId, searchType);ZSetOperations<String, LatelySearchRecordVo> zet = redisTemplate.opsForZSet();Set<LatelySearchRecordVo> redisCacheSet = zet.range(redisCacheKey, pageNum, pageSize);// 3.缓存没有if (StringUtils.isNull(redisCacheSet)){return null;}return redisCacheSet;}public Set<LatelySearchRecordVo> getSearchRecordCacheByLocal(Long userId, SearchTypeEnum searchType, int pageNum, int pageSize) {String localCacheKey = LocalCacheKey.getSearchContentKey(userId, searchType);pageNum = (pageNum - 1) * pageSize;pageSize = pageNum * pageSize - 1;// 1.从内存获取Set<LatelySearchRecordVo> localCacheSet = searchContentCache.getIfPresent(localCacheKey);// 2.内存没有从缓存获取if (StringUtils.isNull(localCacheSet)) {return getSearchRecordCacheByRedis(userId, searchType, pageNum, pageSize);}int maxLength = localCacheSet.size() - 1;if (pageSize > maxLength) {pageSize = maxLength;}return localCacheSet.stream().skip(pageNum).limit(6).collect(Collectors.toSet());}

        缓存没有的从库里查 添加缓存

    @Overridepublic List<LatelySearchRecordVo> latelySearchRecord(SearchTypeEnum searchType, Long userId) {LinkedList<LatelySearchRecordVo> vos = new LinkedList<>();List<SearchHistory> searchRecodeList = searchHistoryMapper.getLatelySearchContent(searchType.name(), userId);searchRecodeList.stream().forEach(recode -> {LatelySearchRecordVo vo = new LatelySearchRecordVo();BeanUtils.copyProperties(recode, vo);vos.add(vo);});if (!StringUtils.isEmpty(vos)){// 写入缓存searchCacheService.addSearchRecord(userId, searchType, vos);}return vos;}
  public void addSearchRecord(Long userId, SearchTypeEnum searchType, List<LatelySearchRecordVo> searchHistory){addSearchRecordLocalCache(userId, searchType, searchHistory);addSearchRecordLocalRedis(userId, searchType, searchHistory);}

       删除搜索记录改库 刷新缓存

    @DeleteMapping("/{id}/{type}")@ApiOperation(value = "删除搜索记录")private AjaxResult removeSearchHistoryNoDisplay(@Validated RemoveSearchHistoryNoDisplayParam param){SearchTypeEnum searchType = verifySearchType(param.getType());searchHistoryService.searchRecodeNoDisplay(param.getId(), SecurityUtils.getUserId(), searchType);return success();}@DeleteMapping("/all")@ApiOperation(value = "删除所有的搜索记录")private AjaxResult removeSearchHistoryAllNoDisplay(@RequestBody RemoveSearchHistoryAllNoDisplayParam param){SearchTypeEnum searchType = verifySearchType(param.getType());searchHistoryService.searchHistoryAllNoDisplay(SecurityUtils.getUserId(), searchType);return success();}
    @Overridepublic void searchRecodeNoDisplay(Long id, Long uid, SearchTypeEnum searchType) {searchHistoryMapper.updateSearchRecodeNoDisplay(id, uid);// 刷新缓存searchCacheService.refreshCache(uid, searchType);}@Overridepublic void searchHistoryAllNoDisplay(Long userId, SearchTypeEnum searchType) {searchHistoryMapper.updateAllSearchRecodeNoDisplay(userId, searchType.name());// 刷新缓存searchCacheService.refreshCache(userId, searchType);}

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

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

相关文章

电脑开机过程中,程序的启动的顺序是怎么样的?

电脑的启动过程涉及多个步骤,程序按照特定的顺序启动。这个过程通常如下: 电源开启: 当你按下电源按钮时,电源供应器(PSU)开始向电脑的各个组件供电。 自检加电(POST): 这是电脑启动过程的第一步。在这个阶段,基本输入输出系统(BIOS)或统一可扩展固件接口(UEFI)执行…

Ubuntu 搭建FTP服务

在Linux中使用的FTP是vsftp&#xff0c;Ubuntu中安装vsftp: apt install vsftpd 配置文件在/etc路径下&#xff0c;会出现两个配置文件&#xff1a; /etc/vsftpd.conf &#xff1a;主配置文件 /etc/ftpusers&#xff1a;指定那些用户不能访问FTP服务器&#xff0c;这里的用…

RunwayGen2上线全新控制功能「运动笔刷」

从8月份开始&#xff0c;我就在各种场合表达过&#xff0c;AI视频现在最痛苦的点&#xff0c;是控制性极差。Gen2的语义理解本来就差&#xff0c;还几乎没有任何可控性&#xff0c;只有那几个镜头控制&#xff0c;剩下全靠roll。但是你roll的&#xff0c;全是白花花的银子啊...…

每日一练:X加上100为完全平方数,再加上168仍然为完全平方数

题目 一个整数&#xff0c;它加上100后是一个完全平方数&#xff0c;再加上168又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 实现方式1 解题思路 设整数为x&#xff0c;根据题意建立方程&#xff1a;   (1) x 100 是一个完全平方数&#xff0c;即存在整数a满…

设计模式-15-Jdk源码中的设计模式

之前我们学习了一些设计模式&#xff0c;今天我们剖析Java JDK 源码中用到的几种常见的设计模式。 1-jdk之工厂模式 在前面讲到工厂模式的时候&#xff0c;大部分工厂类都是以Factory作为后缀来命名&#xff0c;并且工厂类主要负责创建对象这样一件事情。但在实际的项目开发中…

技术分享| gcc版本升级到5.2

一、介绍 GCC&#xff08;GNU Compiler Collection&#xff09;是一套广泛使用的开源编译器集合&#xff0c;用于编译多种编程语言&#xff0c;包括C、C、Objective-C、Fortran等。GCC 的不同版本提供了许多新功能、改进和修复&#xff0c;其中包括从 GCC 4.8.5 升级到 GCC 5.…

泛型边界的问题

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 我们花了两篇文章讲述了…

常见树种(贵州省):007青冈

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、青冈 …

AI一点通:卷积神经网络的输出节点大小如何计算?全连接层必要输入大小如何设置

在使用卷积网络&#xff08;CNN&#xff09;时&#xff0c;一个步骤是计算经过卷积和池化步骤后的输出大小&#xff0c;以便我们可以将输出连接到一个完全收集的线性层。 以Pytorch中的一维CNN为例&#xff0c; self.conv1 nn.Conv1d(in_channels1, out_channels64, kernel_s…

JAVA sql 查询3

-- 1. 求各个月入职的的员工个数 select date_format(hiredate,%m),count(date_format(hiredate,%m)) from employees group by date_format(hiredate,%m) -- 2. 查询 50 号部门,60 号部门,70 号部门的各个部门的平均工资 SELECT department_id,avg(salary) FROM employees WH…

CPSC发布关于亚马逊含有纽扣电池或硬币电池产品的相关规则标准!UL4200A

2023年9月21日&#xff0c;美国消费品安全委员会&#xff08;CPSC&#xff09;在《联邦公报》上发布了纽扣及硬币电池及相关产品的最终规则&#xff08;DFR&#xff09;16 CFR 1263&#xff0c;以保护6岁以下儿童免受电池摄入危害。DFR将于2023年10月23日生效&#xff0c;除非消…

数据库:sql查询值为空的数据

在日常的数据处理中&#xff0c;经常会遇到需要查询某个字段值为空的数据的情况。 针对这种情况&#xff0c;我们可以使用SQL语句来查询值为空的数据&#xff0c;以便更好地对数据进行分析和处理。 一、查询NULL值 在SQL中&#xff0c;NULL表示缺少值或未知值。要查询NULL值…

vue.config.js文件devServer字段的常用选项

在 Vue CLI 生成的 vue.config.js 文件中&#xff0c;devServer 字段用于配置开发服务器的选项。下面是其中几个常用选项的说明&#xff1a; host&#xff1a;指定开发服务器的主机名&#xff0c;默认值是 localhost。你可以将其设置为 0.0.0.0&#xff0c;以允许通过局域网中的…

Unity 控制物体透明度变化

1.需求 给物体绑定一个脚本&#xff0c;这个脚本实现物体的透明度渐变变化&#xff0c;并且可以重置回原来的颜色。物体为Unity自带的材质Shader为Standard。 2.代码 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Transpare…

pgsql 时区查看和修改

建议使用UTC时区&#xff0c;或者和linux、后端程序的时区保持一致&#xff0c;否则容易出现时间的差别。 pgsql的时间字段有一个带时区的timestamp with time zone&#xff0c;如果业务涉及多个时区&#xff0c;建议使用这个字段。 相关链接参考&#xff1a; linux时区设置和…

Git 教程

目录 Git 与 SVN 区别 Git 快速入门 学习目录 git简明指南 Git 安装配置 Git 工作流程、工作区、暂存区和版本库 Git 创建仓库 Git 基本操作 Git 分支管理 Git 查看提交历史 Git 标签 Git 远程仓库(Github) Git 服务器搭建 Git 是一个开源的分布式版本控…

如何写老客户开发信?维护客户邮件怎么写?

老客户开发信的写作技巧&#xff1f;针对老客户的营销邮件模板&#xff1f; 老客户开发信是维护和发展客户关系的关键工具之一。通过巧妙的信函&#xff0c;您可以巩固与老客户之间的联系&#xff0c;促使他们继续购买您的产品或服务。在本文中&#xff0c;蜂邮将分享一些建议…

多线程股吧用户信息爬取

今天分享一些代码&#xff0c;欢迎参考和学习&#xff0c;在上一篇博客的基础上加入了多线程&#xff0c;使得速度提升了十几倍&#xff0c;代码如下&#xff1a; import csv import random import re import threadingimport chardet import pandas as pd from bs4 import Be…

小程序Tab栏与页面滚动联动

小程序tab栏切换与页面滚动联动 tab栏与页面滚动联动点击tab栏页面跳到指定位置滚动页面时切换tab栏 tab栏与页面滚动联动 在进行小程序开发时&#xff0c;需要实现点击tab栏页面滚动到某一指定位置&#xff0c;并且滚动页面时&#xff0c;小程序的tab栏进行切换。 在一开始&a…

黑苹果新手指导:名词解释常用软件常见问题说明

黑苹果新手指导&#xff1a;名词解释&常用软件&常见问题说明 写在前面名词解释系统篇引导篇工具篇 常见问题安装篇如何安装黑苹果&#xff1f;安装过程中卡在一排号怎么办&#xff1f;AMD处理器可以安装黑苹果 macOS吗&#xff1f;我的笔记本电脑为什么不能驱动独立显卡…