面试题-分表+分页+OrderBy如何实现

2020-09-07 01:11:26 浏览数 (1)

"分表后分页",在之前的文章里提过这个点,但是不够详细,最近笔者在工作也遇到了类似的问题,详细的说下这个过程如何实现。

场景:后台系统中的一个订单分页查询功能,乍一看很简单嘛,但是这张订单表分了3张表,分表键是用户标识,比如需要查询第二页的内容并且按照升序排序,该怎么实现呢?

分表后数据分散在各个库,会出现很多种情况:

  1. 数据都在1表或都在2表、2表,极端情况
  2. 数据在1,2,3表完全一致,极端情况
  3. 数据在1,2,3表各自包含一部分,这也是最常见的情况

【假设】一页10条数据,查询第二页的数据,未分表时的实现:

select * from order order by create_time asc limit 10,10;

【假设】分了3张表,分表后的实现:

实现1:全局查询方式

第一步:分表查询0,10 10的数据

select * from order_1 order by create_time asc limit 0,(10 10);

select * from order_2 order by create_time asc limit 0,(10 10);

select * from order_3 order by create_time asc limit 0,(10 10);

第二步:汇总结果,内存排序,获取limit 10,10

把这个三个查询出的List<Order>,汇总到一个List,并且按照时间升序排序,取出第10条到第10 10条的数据。

实现1的优点:满足要求,实现简单,数据完整。

实现1的缺点:需要查询大量的数据,耗时,好网络带宽,汇总结构还需要大量数据的内存排序,耗内存,耗CPU,随着页码增加,比如查第100页,那就需要每张表limit 0,(100 10),很恐怖。

实现2:二次查找

第一步:limit 10,10中,拿到第一个10除以分表数3,结果取整为3;

第二步:

select * from order_1 order by create_time asc limit 3,10;

select * from order_2 order by create_time asc limit 3,10;

select * from order_3 order by create_time asc limit 3,10;

获取到以上3个List<Order>,分别取出第一条数据的时间,按照时间比较后取到最小的一条数据,min_create_time;

第三步:

select * from order_1 where between min_create_time and order1_min_create_time;

select * from order_2 where between min_create_time and order2_min_create_time;

select * from order_3 where between min_create_time and order3_min_create_time;

其中每条Sql里的orderX_min_create_time,是第二步每个List的第一条数据的时间,获取到以上sql的查询结果List<Order>.

第四步:

找到min_create_time在各个表的位置,也就确定了min_create_time在全局表的位置,合并集合就能找到limit 10,10的数据了.

第二种方式,比较巧妙,在数据极端的情况下会有问题,但是正常来说不会出现这种情况的数据情况,如下图

0 人点赞