redis分页缓存--解决了pageNum与size切换造成的资源浪费问题

2 篇文章 0 订阅
1 篇文章 0 订阅

redis分页缓存--解决了pageNum与size切换造成的资源浪费问题

最近接到一个需求,需要对不规则数据进行一个分页缓存。

不规则!!!

顾名思义就是,你根本不知道要缓存的数据结构长啥样,有什么字段内容!!!

网上看过好多篇文章都不适用的,干脆自己倒腾一下。并且发现网上的例子存在很多的bug。

例如数据都存放到一个key里面了,容易造成bigKey影响性能,

用户切换或pageNum或size之后无法命中缓存,

缓存的数据没有顺序。

接下来就有了自研版的redis分页缓存,使用zset有序集合来实现,代码思路:
control:

		//请求传来的参数--pageNum当前页码--pageSize大小
		long pageStart = ((long) (pageNum - 1) *pageSize);
		//组装key--可根据具体项目拼接key,这里简单举例
        //总数据量
        String totalCountKey = "TOTAL_COUNT";
        //存储目录
        String cacheDataKey = "SOURCE_TABLE_LIST";

        //有缓存取缓存,无缓存查库
        Object totalCountObj = cacheManager.get(totalCountKey);
        if (!ObjectUtils.isEmpty(totalCountObj)) {
            //取出缓存重点看这里开始!!!
            //计算需要的缓存数据
            Long totalCount = Long.valueOf((String) totalCountObj);
            int currentPage = pageNum;
            if (totalCount<=pageStart){ //防止size切换,造成当前页*size超出边界
                currentPage = Math.min(1,(int)Math.ceil((double) pageStart / (double) pageSize)+1);
                pageStart = ((long) (currentPage - 1) *pageSize);
            }
            //留给大家发挥的空间,根据计算结果,统计结果缓存区间中数据量是否小于所需的数据量且小于等于totalCount,是则分页查库,把空缺的数据插入缓存
            //使用计算后结果取出对应位置缓存数据
            Set<Object> tuples = cacheManager.rangeByScore(cacheDataKey, (double) pageStart, (double) pageStart + pageSize);
            //类型转换,无需理会,各自发挥
            List<TableInfo> cacheDataList = PortalBeanUtils.convertList(Arrays.asList(tuples.toArray()), TableInfo.class);
            if (totalCount != null && cacheDataList != null && cacheDataList.size()>0) {
                //将计算后的当前页码返回给前端,以及保证totalCount是缓存刷新后数据库中的实际数据条数
                return ResponseEntity.ok(new PaginatedResult()
                        .setCode(SuccessCode.DATA_CHECK_SUCCESS.getCode())
                        .setMessage(SuccessCode.DATA_CHECK_SUCCESS.getMessage())
                        .setData(cacheDataList)
                        .setCurrentPage(currentPage)
                        .setPageSize(pageSize)
                        .setTotalCount(totalCount));
            }
        }
        
		//无缓存查库并将结果插入缓存
		//查库代码开始--无需理会------------------------------------------------------!
        Object[] builderResult = SourceDatabaseServiceImpl.buildSql(sourceDatabase);

        String sqlBody = (String) builderResult[0];

        //获取连接--动态数据源查询
        JdbcUtils jdbc = JdbcUtils.newJdbcUtils(sourceDatabase);

        String pageSql = jdbc.getLimitString(sqlBody, pageStart, pageSize);
        Object[] params = (Object[]) builderResult[1];
        List<LinkedHashMap<String, Object>> data = jdbc.selectSortList(pageSql, params);

        List<Object> tableInfoList = new ArrayList<>();
        for (Map<String, Object> rsMap: data) {
            TableInfo tableInfo = PortalBeanUtils.mapToBean(rsMap, TableInfo.class, true);
            tableInfoList.add(tableInfo);
        }

        String countSql = (String) builderResult[2];
        PageHelpDto pageHelpDto = jdbc.pageHelp(countSql, params, pageStart, pageSize);
		//查库代码结束--无需理会--------------------------------------------------------------------!
        //异步加入缓存
        long finalPageStart = pageStart;
        Thread thread = new Thread(() -> {
            cacheManager.set(totalCountKey, pageHelpDto.getTotalCount(), 30L, TimeUnit.MINUTES);
            //插入缓存重点看这里!!!
            cacheManager.zAdd(cacheDataKey, tableInfoList, (double) finalPageStart,30L); //todo test设置30分钟过期
        });
        thread.start();

插入缓存逻辑zAdd:

    /**
     * 同时将多个值存入有序集合中。同时为每个值设置相同的生存时间
     * @param key 键
     * @param objectList 值
     * @param times 生存时间(分钟)
     */
    public void zAdd(String key, List<Object> objectList, double stratScore, Long times) {
        int i = 0;
        for (Object obj : objectList) {
            Double stratScoreInc = stratScore + i;
            redisTemplate.opsForZSet().add(key, obj, stratScoreInc);
            i++;
        }
        redisTemplate.expire(key, times, TimeUnit.MINUTES);
    }

取出缓存逻辑rangeByScore:

    /**
     * 获取有序集合中的缓存
     * @param key 键
     * @param stratScore 开始下标
     * @param endScore 结束下标
     */
    public Set<Object> rangeByScore(String key, Double stratScore, Double endScore){
        Set<Object> values = redisTemplate.opsForZSet().rangeByScore(key, stratScore, endScore);
        return values;
    }

初版代码,有bug欢迎吐槽~
转载请注明出处!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值