如何实现类似知乎的功能查看最近1000条记录呢

引言

在很多地方都可以看到自己的最近商品浏览记录,要么保持最近1000条记录(如知乎的最近浏览记录),要么是100条,那么如何在不创建多余的表而直接用缓存实现呢?

基础要求极其思路

  • 要求
    1.技术选型:redis
    2.在每次浏览的时候去缓存中记录这条记录的ID
    3.确保记录最大条数只能达到设置的阈值,比如1000
    4.记录不能重复且按照时间倒叙排序,举例:昨天浏览的记录,今天再次浏览时之前的记录删除,只保留今天浏览的记录时间
    5.持久化,浏览记录持久存在
  • 思路
    1.使用redis的zset,用户唯一ID作为key,用户访问记录ID作为value,访问时间作为分数score
    2.每次浏览记录向该用户集合中插入一条记录,插入前判断是否有旧的记录,有则删除,然后添加,每次插入的记录时间戳递增,借助zset本身的有序性,很容易实现查询排序翻转
    3.插入后获取总记录,如果大于阈值,则删除阈值之外更旧的记录,时间倒序

实现步骤

  • 1.以用户唯一ID作为redis的key,数据结构使用ZSET,首先插入模拟数据200条,其中value为当前用户浏览的记录ID,score为插入时的时戳,中间有休眠模拟用户点击操作,间隔时间为1秒。
String key = "wangsr0001";
for (int i = 0; i < 200 ; i++) {
            TimeUnit.SECONDS.sleep(1L);
            String randomChineseName = RandomPersonInfoUtil.getRandomTelephoneNumber();
            redisTemplate.opsForZSet().add(key,randomChineseName,System.currentTimeMillis());
 }
  • 2.开始模拟第201条数据插入,最后面删除操作时,因为时间最早的排在最前面,所以删除索引是从0开始,
 /**
     * 新阅读记录插入
     * @author wjl
     */

    @Test
    public void insertTest(){
       //key
        String key = "wangsr0001";
       //阈值
        long top  = 200;
       //新访问记录ID
        String value  = "15646051140";
        Double score = redisTemplate.opsForZSet().score(key, value);
        //检索是否有旧记录  1.无则插入记录值  2.有则删除 再次插入
        if(null != score){
            //删除旧的
            redisTemplate.opsForZSet().remove(key,value);
        }
        //加入新的记录,设置当前时间戳为分数score
        redisTemplate.opsForZSet().add(key,value,System.currentTimeMillis());
        //获取总记录数
        Long aLong = redisTemplate.opsForZSet().zCard(key);
        if(aLong > top){
            //获取阈值200之后的记录  (0,1] 并移除
            redisTemplate.opsForZSet().removeRange(key,0,aLong-top-1);
        }
    }

  • 3.列表查询历史记录,倒序
/**
     * 列表查询
     * @author wjl
     */
    @Test
    public void scanTest(){
        String key = "wangsr0001";
        long start = 1;
        long size  = 10;
        Set<ZSetOperations.TypedTuple> scoreWithScores = redisTemplate.opsForZSet().reverseRangeWithScores(key, start - 1, size - 1 );
        Iterator<ZSetOperations.TypedTuple> iterator = scoreWithScores.iterator();
         BigDecimal bigDecimal = null;
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple next = iterator.next();
            bigDecimal = BigDecimal.valueOf(next.getScore());
            System.out.println("==》ID: "+next.getValue()+" 时间: "+bigDecimal.toPlainString());
        }
    }
  • 结果对比(上述例子查询的是最新的十条)
  • 控制台输出查询
  • redis中的数据对比

最后

  • 求赞赞,求关注,有问题请留言
  • 文章来源公众号 苏克分享
    苏克分享