一、如何使用 Redis 来缓存来实现最近浏览的商品列表?
首先我们要确定一个两个点,最近浏览的商品肯定是一个存一个取的操作。
那么就可以确定以下几个问题:
-
最近浏览的记录肯定是要有失效时间的
这里可以使用缓存(Redis等),缓存可以设置失效时间(最大设置为一个月)。
如果使用关系型数据库,还需要定时清楚,就很不符合实际需求。
-
最近浏览记录肯定是要有个数限制的,不可能记录所有的浏览记录
如果使用Redis来实现的话,Redis 中有 LTRM 来修建,以保证存储的浏览条数。
-
需要在哪里添加保存浏览商品的方法。
用户最近浏览的商品,肯定是要在用户打开商品详情页的时候才算浏览。
-
怎么保证每次添加的浏览的商品列表按照浏览的先后顺序排序?
每次用户的浏览商品的ID,可以以用户的ID作为Key,以 List 作为 value,存储在Redis中,
而 List 是有序的,并且,在使用 LRANGE 的时候还能保证先进后出,后进先出的原则,以
达到排列在最前面的商品始终是当前最近浏览的商品。
-
怎么保证用户在连续浏览同一商品的时候,不会重复保存商品?
这里可以使用 Redis 中的 LREM 来移除例表中与参数 value(该商品ID) 想等的元素。
同时再使用 Lpush 重新再List 中插入最新浏览的商品。
-
读取缓存的时候,怎么保证分页?
Redis 中的 LRANGE 可以指定获取指定长度的元素。
-
怎么区别 PC 和 移动端?
可以让前端根据 PC 和 移动端传不同的code,后台区别code,在 key(用户ID)后面跟上
特定的字符串来区别。
二、下面是简单的实现思路
-
存储用户浏览的商品信息:
用户在打开商品详情页的时候,以用户ID作为key,商品的Id作为值,以List的形式存到Redis中。
在加入缓存之前,为了确保浏览商品的唯一性,每次添加之前,使用 LREM 将缓存 List 中的商品id去掉,以保证最新的浏览记录始终在最前面。
在 Lpush 到 Redis 的list中之后,可以根据需求 保留List中多少条浏览记录
最后添加缓存失效时间。
-
获取用户最近的浏览商品列表:
根据key(用户ID)及分页数,来获取商品缓存。
三、下面是截取的一段核心代码:
-
根据用户ID和商品Id存入到缓存中:
/** * 存一个浏览商品信息 * * @param historyPo */ @PostMapping("/set") public void set(@Validated @RequestBody HistoryPo historyPo) { String key = getKey(historyPo.getCode(), historyPo.getUserId()); /* 为了保证浏览商品的 唯一性,每次添加前,将list中该商品ID去掉,再加入,以保证其浏览的最新的商品在最前面 */ redisService.lrem(key, 1L, historyPo); /* 将value push 到该key下的list中 */ redisService.lpush(key, historyPo); /* 浏览记录存5条,五条以后切掉*/ redisService.lTrim(key, 0L, 4L); /* 缓存时间为一个月 */ redisService.expire(key, DEFAULT_EXPIRE); }
-
根据用户的ID,分页获取最近浏览的商品:
/** * 获取当前用户的浏览历史 * * @param code * @param userId * @return */ @GetMapping("/all/{code}/{userId}") public List<HistoryPo> query(@PathVariable("code") Integer code, @PathVariable("userId") Long userId) { String key = getKey(code, userId); //这里可支持分页 start and end return redisService.lrange(key, 0L, 9999L); /* 以下代码解决json反序列化之后集合不能操作问题(例如使用Stream) String key = getKey(code, userId); List<HistoryPo> lrange = redisService.lrange(key, 0L, 9999L); JavaType javaType = getCollectionType(LinkedList.class, HistoryPo.class); List<HistoryPo> lst = null; try { lst = mapper.readValue(lrange.toString(), javaType); } catch (IOException e) { e.printStackTrace(); } */ }