正确的Redis数据类型,让访问时间提高几十倍

1,101 阅读2分钟

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

问题背景

接到了一个需求,是通过物品查询分类中涨价排行最高的N个子类,再查询每个子类对应的涨价排行最高的N个物品。由于数据结构是hash类型,全部物品、子类物品-价格、物品-子类、子类排行。 看到了物品对应子类集合和子类-价格集合,通过取交集,能将物品对应的子类排行查出来,尝试使用redis的zset来实现。

但是并发量上来之后,发现redis取交集这个操作很耗时。于是用hash结构保存数据,再用java进行排序,代码多了好几行,但是并发查询的时间大幅度缩短。

Redis的数据类型可参考: # 【简约入门】Redis的数据类型及常用命令https

原始代码

	// 取goods与category的交集,结果放到result中		
        redisTemplate.opsForZSet().intersectAndStore(goods,category,result);
        int start = (pageNum-1) * pageSize;
	// 分页获取集合
        Set<ZSetOperations.TypedTuple<Object>> rangeWithScores = redisTemplate.opsForZSet().reverseRangeWithScores(result, start, pageSize - 1);
        System.out.println(rangeWithScores);

优化后代码

        // 查询goodId对应的类别
	List<Object> categoryList = (List<Object>) redisHash.hGet("good:categorys",goodId);
        // 查询上面查出类别的价格
	List idxPriceList = redisHash.multiGet("category",categoryList);

        List<Map> idxList = new ArrayList<>();
        for (int i = 0;i<boardList.size();i++){
            Map<String, Object> idxM = new HashMap<>();
            idxM.put("k",boardList.get(i));
            idxM.put("v",idxPriceList.get(i));
            idxList.add(idxM);
        }
	// 种类按价格排序
        idxList.sort((o1, o2) -> {
                return (o1.get("v").toString()).compareTo(o2.get("v").toString());
        });
	System.out.println(idxList);
       // 获取最高物品的代码
       String code = idxList.get(0).get("k").toString();
        // 种类下的物品
        List<Object> sList = (List<Object>) redisHash.hGet("category:goods",code);

        // 查找物品对应价格
        List codeList = redisHash.multiGet("good:price",sList);

        List<Map> codeIdxList = new ArrayList<>();
        for (int i = 0;i<sList.size();i++){
            Map<String, Object> idxM = new HashMap<>();
            if(null != codeList.get(i)){
                idxM.put("k",sList.get(i));
                idxM.put("v",codeList.get(i));
                codeIdxList.add(idxM);
            }
        }

       // 按价格排序
        codeIdxList.sort((o1, o2) -> {
                return (o1.get("v").toString()).compareTo(o2.get("v").toString());
        });
        // 分页获取数据
        codeIdxList = codeIdxList.stream().limit(pageSize).collect(Collectors.toList());
        
        System.out.println(codeIdxList);

小结

当并发30、50的时候,使用redisTemplate.opsForZSet().intersectAndStore取交集的平均耗时是超过500ms的,并且尝试使用spring注解,加载多个redisTemplate来取交集,发现每个执行时间还是一样,如果只是取交集还可以,由于数据结构复杂需要多个交集操作,多个查询操作,使得整体的查询时间很长,2-3s。

改变java代码排序,取出最高的种类,再取出这个种类下的物品,效率更高,查询结果可控制在100ms左右。