在项目中开发一个接口,这个接口是一个大sql查询,数据库使用的是mongodb,一次性查询5千条以上的数据,这就带来了一个问题,就是查询效率的问题,会很慢,要等5秒以上才能等到数据,尽管对关键字段都做了索引,还是很慢。也许你会说不要一次性查这么多数据,搞个分页不就行了吗?外卖这种情况还真不行,得把所有数据拿到才可以进行计算。
为了提高查询效率,就想到要么把数据预先存到redis里面去,到时候直接读取redis不就快了吗?我们真这样做了,结果发现redis读取这种大key的时候发现依然比较慢,那么怎么办?
思想:预先将大sql查询出来的数据拆分成多个小集合,缓存在redis里面,在查询的时候使用多线程异步去读取这几个缓存在redis里面的集合数据,再将读取到的数据组装成一个新的大集合,如此大大提高了数据读取效率,那么问题来了,在使用多线程多去redis里面的数据的时候,如何判断所有线程已经将数据都已经取完了呢?我这边做了一个自旋判断,只有将读取到数据数量等于之前缓存到redis中的数量,那么就认为数据已经取完了,否则就一直等知道所有线程都读取完,当然,在遇到网络不好的情况下,可能遇到redis读取很慢,那我们也不能一直自旋等待下去,就需要设置一个等待超时时间。不多说了,上代码:
1、把查询出来的数据缓存到redis Criteria criteria = Criteria.where("code").is(code).and("time").lte(endDate);
Query stockQuery = new Query(criteria);
stockQuery.with(Sort.by(Sort.Order.asc("time")));
stockQuery.fields().exclude("id", "turnoverRatio", "amount", "change", "changeRatio");//id
myStockPools = mongoTemplate.find(stockQuery, MyStockPool.class, "myStockPool");
int groupNum = (int) Math.ceil((double) myStockPools.size() / StockConstant.PAGE_SIZE_D_800);
redisTemplate.opsForValue().set(StockConstant.CODE_GROUP_NUM + code, String.valueOf(groupNum));
if (groupNum > StockConstant.ONE) {
//拆成多个组,使用多线程去去读取数据
for (int i = 1; i <= groupNum; i++) {
List reslist = pageBySubList(myStockPools, StockConstant.PAGE_SIZE_800, i);
redisTemplate.opsForValue().set(StockConstant.CODE + code + i, JSON.toJSONString(reslist));
log.info("reslist size:{}", reslist.size());
}
log.info("mappedResults end:{}", LocalDateTime.now());
}
2、使用异步多线程读取redis分段数据
public List暑期编程PK赛 得CSDN机械键盘等精美礼品!mongoQuotaData(String endDate, String code) { List myStockPools = new ArrayList<>(); String codeGroupNum = redisTemplate.opsForValue().get(StockConstant.CODE_GROUP_NUM + code); if (StringUtils.hasText(codeGroupNum)) { int goupNumRedis = Integer.parseInt(codeGroupNum); CompletableFuture [] jasc = new CompletableFuture[goupNumRedis]; log.info("redisList start:{}", LocalDateTime.now()); CountDownLatch countDownLatch = new CountDownLatch(goupNumRedis); for (int i = 0; i < goupNumRedis; i++) { String key = StockConstant.CODE + code + (i + 1); //异步调用 List finalMyStockPools = myStockPools; jasc[i] = CompletableFuture.runAsync(() -> { try { String str = redisTemplate.opsForValue().get(key); if (StringUtils.hasText(str)) { List redisMyStockPools = JSON.parseArray(str, MyStockPool.class); log.info("redismyStockPools size:{}", redisMyStockPools.size()); if (CollectionUtil.isNotEmpty(redisMyStockPools)) { finalMyStockPools.addAll(redisMyStockPools); } } } finally { //当前线程执行完成之后减一 countDownLatch.countDown(); } }, executor); } log.info("redisList start:{}", LocalDateTime.now()); try { //阻塞等待所有的线程都执行完成才会进行下一步操作,如果超过5秒还没有完成就不等了 countDownLatch.await(5,TimeUnit.SECONDS); log.info("countDownLatch完成了:{}", countDownLatch.getCount()); myStockPools = myStockPools.stream().sorted(Comparator.comparing(MyStockPool::getTime)).collect(Collectors.toList()); AtomicInteger index = new AtomicInteger(0); myStockPools.stream().filter(s -> { //每比对一个元素,数值加1 index.getAndIncrement(); return s.getTime().equals(endDate); }).findFirst(); int end = index.get(); //截取该日期之前的数据 myStockPools = myStockPools.subList(0, end); } catch (InterruptedException e) { e.printStackTrace(); } } }



