多数据源分页查询

多数据源归并分页:

请求链接为:/user/holding/list?userId={}&rows={}&limitTime={}

limitTime:代表最后一条的时间

从A、B中各取N条数据,合并后取时间戳最大的前N条,核心代码如下:

List<Resp> queryPagedListByLimitTime(long userId, int rows, long limitTime) {
    List<Resp> totalList = new ArrayList<>();
    totalList.addAll(aService.getListByLimitTime(userId, rows, limitTime));
    totalList.addAll(bService.getListByLimitTime(userId, rows, limitTime));
    return totalList.stream()
                    .sorted(Comparator.comparing(Resp::getCreateTime).reversed())
                    .limit(rows)
                    .map(/* do business */)
                    .collect(Collectors.toList());
}

存在bug:这个实现简单而高效,但是上线后发现有丢数据的情况。因为系统有批量下单功能,导致许多持仓数据的createTime字段毫秒值都相同。而查询时传入当页的最后一条时间戳,因此下一页中按小于此时间戳查询,就丢失了跨页的数据。

Redis归并排序分页:

/user/holding/list?userId={}&pageNo={}&rows={}

首先查询逻辑不变,但是现在需要尽可能查出所有数据完成排序。因此将用户数据缓存到Redis的ZSET中,score是用于排序的字段(时间戳)。

List<Resp> queryPagedListFromRedis(long userId, int rows, int pageNo) {
    String userSetKey = RedisConstants.HOLDING + "userId:" + userId;
    BoundZSetOperations<String, Resp> boundZSetOperations = redisTemplate.boundZSetOps(userSetKey);
    Set<ZSetOperations.TypedTuple<Resp>> totalRemainSet = boundZSetOperations.reverseRange(pageNo * rows, (pageNo + 1) * rows);
    return totalRemainSet.stream()
              .map(ZSetOperations.TypedTuple::getValue)
              .collect(Collectors.toList());
}

这个实现存在几个问题:

  1. 需要在用户首次进入时获取全量数据,效率无法保证。
  2. 数据放在缓存中,需要更新维护,增加了系统复杂度。
  3. (对于旧客户端的兼容)破坏了接口的约定,请求传入rows=15返回却可能是rows=200。

覆盖索引,第三个实现

第二个方案虽然未上线,但是思路有可取之处。总结上面两个方案可知:

  1. 由于时间戳有重复,因此以limitTime做入参是不可行的,需要分页方式查询。
  2. 由于数据源不同,因此需要以相同排序条件查出后归并。但是若通过标记id等辅助分页字段方式分页,则需要增加接口字段,增加复杂度。
  3. 全量数据归并后排序就不需要辅助字段,可保持接口参数不变,但是需要高效的查询全量数据方式。

由于排序时仅需要根据createTime排序,因此获取全量数据可改为仅获取id和createTime两个字段,排序后再通过id查询信息。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值