高端品牌网站建设内容,富平网站建设,教育机构官网,深圳电商网站制作公司目录 一、前言二、分析问题三、针对两个问题#xff0c;使用redis怎么解决问题#xff1f;1、字符串String2、列表List3、字典Hash4、集合Set5、有序集合ZSet6、需要解决的五大问题 四、编写代码1.pom依赖2.application.yml配置3.Product商品实体4.用户最近搜索信息5.redis辅… 目录 一、前言二、分析问题三、针对两个问题使用redis怎么解决问题1、字符串String2、列表List3、字典Hash4、集合Set5、有序集合ZSet6、需要解决的五大问题 四、编写代码1.pom依赖2.application.yml配置3.Product商品实体4.用户最近搜索信息5.redis辅助类SearchRedisHelper6.业务service7.controller控制层 五、postman测试1.第一次搜索2.热点搜索3.最近搜索4.第二次第三次搜索5.再看热点搜索6.再看最近搜索变化7.第四次搜索8.热搜变化9.最近搜索变化 六、总结 一、前言
大家在浏览各种网站比如淘宝京东微博等网站都会看到一些热门搜索和最近搜索的功能大家有木有好奇技术背后是如何实现的呢今天我们一起来用redis解决这两个问题并已在项目中实战 热搜如下图 最近搜索如下图: 二、分析问题
1、热门搜索是指一定时间内、一定范围内公众较为关心的热点问题被搜索的次数越多热搜榜越靠前。
2、最近搜索只显示当前用户最近一段时间内的搜索记录按照时间进行排序如果有重复搜索覆盖到重复的数据并且要排到最前面。
3、针对于热门的搜索属于高并发的场景还需要高性能显示给用户用MySQL存储显然不太合适流量过多会把MySQL撑爆最近搜索和最热搜索也不需要持久化最好的解决方案之一就是redis做缓存单机redis可以承受10万QPS。
三、针对两个问题使用redis怎么解决问题
我们复习一下redis的五大数据类型redis数据类型可以参考Redis中5种基本数据类型结构详解
1、字符串String
特性 1最基本的数据类型二进制安全的字符串最大512M。 2支持字符串操作:strlen或取value的长度返回的是字节的数量。 3数据交互有个二进制安全的概念给我数据的时候你自己编码字节数组到达我这里整理帮你存客户端之间商量好。 4支持数值计算操作:incr,decr 应用场景做简单得键值对缓存比如Sessiontoken统计限流轻量级kb级别的FS内存级的文件系统—任何东西都可以变成字节数组二进制一些复杂的计数功能的缓存
2、列表List
特性 按照添加顺序保持顺序的字符串列表也就是存储一些列表型得数据结构类似粉丝列表、文字得评论列表之类得数据。 应用场景 可以做简单的消息队列的功能。另外还有一个就是可以利用lrange命令做基于redis的分页功能性能极佳用户体验好。
3、字典Hash
特性 1key-value对的一种集合存储结构化得数据比如一个对象。 2这里value存放的是结构化的对象比较方便的就是操作其中的某个字段。 应用场景 经常会用来做用户数据的管理存储用户的信息。比如做单点登录的时候就是用这种数据结构存储用户信息以cookieId作为key设置30分钟为缓存过期时间能很好的模拟出类似session的效果。
4、集合Set
特性: 无序的字符串集合不存在重复的元素. 应用场景 去重还可以利用交集、并集、差集等操作可以计算共同喜好全部的喜好自己独有的喜好等功能。
5、有序集合ZSet
特性 已排序的字符串集合。去重并排序如获取排名前几名。 应用场景 sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用取TOP N操作。
6、需要解决的五大问题
问题一很显然根据咱们的以上分析热门搜索和最近搜索的功能需要去重并且排序热门搜索点击率最高的在前面最近搜索最新的数据搜索在最前面所以使用ZSet集合实现最合适。针对于最近搜索的功能使用List也可以实现但是删除的效率要比ZSet慢还需要自己去重所以还是Zset最合适。
问题二用户可能无限制浏览商品最近搜索的功能需要确保zSet 不能无限制插入需要控制zSet 的大小也就是指保存最近N条浏览记录。
问题三最近搜索的功能需要在插入第N1 条后移除最开始浏览的第一条。
问题四热门搜索key值需要过期时间的。
问题五热门搜索针对的是所有用户而最近搜索针对的是当前用户。
以上五大问题均在代码中详细解决仔细看注释。
四、编写代码
1.pom依赖
!-- redis --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency2.application.yml配置
server:port: 8889spring:redis:host: 127.0.0.1port: 6379password: database: 2timeout: 50003.Product商品实体
Data
public class Product implements Serializable {//商品idprivate Long id;//商品名称private String productName;//.....等属性
}4.用户最近搜索信息
Data
public class UserRecentSearch implements Serializable {/*** 搜索信息*/private String searchInfo;/*** 用户id*/private Long unionId;
}5.redis辅助类SearchRedisHelper
Component
public class SearchRedisHelper {Resourceprivate RedisTemplate redisTemplate;/*** 热搜的KEY*/public static final String HOT_SEARCH product_hot_search;/*** 最近搜索的KEY*/public static final String RECENT_SEARCH product_recent_search;/*** 最近搜索的大小*/public static final Integer CURRENT_SEARCH_SIZE 3;/*** 最热搜索KEY过期时间*/public static final Integer HOT_SEARCH_EXPIRE_TIME 3;/*** 设置redis的过期时间* expire其实是懒加载不设置key的时候是不会执行的*/PostConstructpublic void setHotSearchExpireTime() {redisTemplate.expire(HOT_SEARCH, HOT_SEARCH_EXPIRE_TIME, TimeUnit.SECONDS);}/*** redis添加最近搜索** param query*/public void addRedisRecentSearch(String query) {UserRecentSearch userRecentSearch new UserRecentSearch();// 用户id当前用户iduserRecentSearch.setUnionId(100434L);// 搜索信息userRecentSearch.setSearchInfo(query);// score为一个分值需要把最近浏览的商品id的分值设为最大值// 此处我们可以设置为当前时间Instant.now().getEpochSecond()// 这样最近浏览的商品id的分值一定最大排在Zset集合最前面ZSetOperationsString, UserRecentSearch zSet redisTemplate.opsForZSet();// 由于zSet的集合特性当插入已经存在的V值商品id时只会更新score值zSet.add(RECENT_SEARCH, userRecentSearch, Instant.now().getEpochSecond());// 获取到全部用户的最近搜索记录用reverseRangeWithScores方法可以获取到根据score排序之后的集合SetZSetOperations.TypedTupleUserRecentSearch typedTuples zSet.reverseRangeWithScores(RECENT_SEARCH, 0, -1);//只得到当前用户的最近搜索记录,注意这里必须保证set集合的顺序SetUserRecentSearch userRecentSearches listRecentSearch();if (userRecentSearches.size() CURRENT_SEARCH_SIZE) {//获取到最开始浏览的第一条UserRecentSearch userRecentSearchLast userRecentSearches.stream().reduce((first, second) - second).orElse(null);//删除最开始浏览的第一条zSet.remove(RECENT_SEARCH, userRecentSearchLast);}}/*** 热搜列表* return*/public SetProduct listHotSearch() {//0 5 表示0-5下标对应的元素return redisTemplate.opsForZSet().reverseRangeWithScores(HOT_SEARCH, 0, 5);}/*** redis添加热搜* param productList*/public void addRedisHotSearch(ListProduct productList) {//1表示每调用一次当前product的分数1productList.forEach(product - redisTemplate.opsForZSet().incrementScore(HOT_SEARCH, product, 1D));}/*** 最近搜索列表* return*/public SetUserRecentSearch listRecentSearch() {SetZSetOperations.TypedTupleUserRecentSearch typedTuples redisTemplate.opsForZSet().reverseRangeWithScores(RECENT_SEARCH, 0, -1);return Optional.ofNullable(typedTuples).map(tuples - tuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull)
// .filter(userRecentSearch - Objects.equals(userRecentSearch.getUnionId(), ContextHolder.getUser().getId())).filter(userRecentSearch - Objects.equals(userRecentSearch.getUnionId(), 100434L)).collect(Collectors.collectingAndThen(Collectors.toCollection(LinkedHashSet::new), LinkedHashSet::new))).orElseGet(LinkedHashSet::new);}}6.业务service
Service
public class ProductService {Resourceprivate SearchRedisHelper searchRedisHelper;/*** 搜索* param query* return*/public ListProduct search(String query) {//业务代码可用es.....此处略过....模拟数据库数据ListProduct productList new ArrayList();Product product new Product();product.setId(1L);product.setProductName(iphone15);productList.add(product);searchRedisHelper.addRedisRecentSearch(query);searchRedisHelper.addRedisHotSearch(productList);return productList;}/*** 热搜列表* return*/public SetProduct listHotSearch() {return searchRedisHelper.listHotSearch();}/*** 最近搜索列表* return*/public SetUserRecentSearch listRecentSearch() {return searchRedisHelper.listRecentSearch();}
}7.controller控制层
RequestMapping(/redis/test)
RestController
public class RedisController {Resourceprivate RedisTemplate redisTemplate;Resourceprivate ProductService productService;/*** 删除redis* param key* return*/GetMapping(/w/remove/redis)public Result removeRedis(String key){redisTemplate.delete(key);return Result.success();}/*** 搜索* param query* return*/GetMapping(/r/search/product)public Result listProduct(String query) {return Result.success(productService.search(query));}/*** 热搜列表* return*/ResponseBodyGetMapping(/r/list/hot/search)public Result listHotSearch() {return Result.success(productService.listHotSearch());}/*** 最近搜索列表* return*/ResponseBodyGetMapping(/r/list/recent/search)public Result recentHotSearch() {return Result.success(productService.listRecentSearch());}}五、postman测试
1.第一次搜索 2.热点搜索 3.最近搜索 4.第二次第三次搜索 5.再看热点搜索 6.再看最近搜索变化 7.第四次搜索
再搜索两次
8.热搜变化 9.最近搜索变化 六、总结
本文针对于网站热点搜索和最近搜索的问题对redis的五大数据类型进行了解读并且采用高并发利器redis的ZSet有序集合完美解决本文一开始引入的问题保证了系统的高并发和高性能提高用户体验。
项目代码Github
推荐好文 Redis实现分布式锁详细方法
如果看到这里说明你喜欢这篇文章请转发点赞