"分表后分页",在之前的文章里提过这个点,但是不够详细,最近笔者在工作也遇到了类似的问题,详细的说下这个过程如何实现。
场景:后台系统中的一个订单分页查询功能,乍一看很简单嘛,但是这张订单表分了3张表,分表键是用户标识,比如需要查询第二页的内容并且按照升序排序,该怎么实现呢?
分表后数据分散在各个库,会出现很多种情况:
数据都在1表或都在2表、2表,极端情况
数据在1,2,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,分别取出第一条数据的时间,按照时间比较后取到最小的一条数据,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的数据了.
第二种方式,比较巧妙,在数据极端的情况下会有问题,但是正常来说不会出现这种情况的数据情况,如下图
感谢各位老铁阅读,更多请关注公众号:别明天就今天吧