11、ShardingJDBC实战:分表后分页排序问题

通常的分页: select * from table order by ** limit offset,pagesize ;

一 问题

部分测试代码,模拟取3页的。

 List<PaymentPay>  plist =paymentPayService.selectByPage(queryPayDto);
	  System.out.println(plist.size());
	  for(int i=0;i< plist.size();i++)
	  {
		  PaymentPay pay =plist.get(i);
		  System.out.println(pay.getPayOrderId()+":"+pay.getCreateTime().toLocaleString());
	  }
	  queryPayDto.setOffset(2);
	  plist =paymentPayService.selectByPage(queryPayDto);
	  System.out.println(plist.size());
	  for(int i=0;i< plist.size();i++)
	  {
		  PaymentPay pay =plist.get(i);
		  System.out.println(pay.getPayOrderId()+":"+pay.getCreateTime().toLocaleString());
	  }
	  queryPayDto.setOffset(3);
	  plist =paymentPayService.selectByPage(queryPayDto);
	  System.out.println(plist.size());
	  for(int i=0;i< plist.size();i++)
	  {
		  PaymentPay pay =plist.get(i);
		  System.out.println(pay.getPayOrderId()+":"+pay.getCreateTime().toLocaleString());
	  }

测试结果:

10
318410639322124402:2018-5-28 15:30:12
318410988967694355:2018-5-28 15:31:36
318410991522025492:2018-5-28 15:31:36
318410991714963464:2018-5-28 15:31:36
318410991983398922:2018-5-28 15:31:36
318410992222474332:2018-5-28 15:31:36
318410992398635107:2018-5-28 15:31:36
318410992541241414:2018-5-28 15:31:36
318410992679653457:2018-5-28 15:31:36
318410992960671822:2018-5-28 15:31:37
10
318410988967694355:2018-5-28 15:31:36
318410991522025492:2018-5-28 15:31:36
318410991714963464:2018-5-28 15:31:36
318410991983398922:2018-5-28 15:31:36
318410992222474332:2018-5-28 15:31:36
318410992398635107:2018-5-28 15:31:36
318410992541241414:2018-5-28 15:31:36
318410992679653457:2018-5-28 15:31:36
318410992960671822:2018-5-28 15:31:37
318410993120055362:2018-5-28 15:31:37
10
318410991522025492:2018-5-28 15:31:36
318410991714963464:2018-5-28 15:31:36
318410991983398922:2018-5-28 15:31:36
318410992222474332:2018-5-28 15:31:36
318410992398635107:2018-5-28 15:31:36
318410992541241414:2018-5-28 15:31:36
318410992679653457:2018-5-28 15:31:36
318410992960671822:2018-5-28 15:31:37
318410993120055362:2018-5-28 15:31:37
318410993254273071:2018-5-28 15:31:37
InitTest init...test

可见单页内有序,分页的上一页最大id比下一页的开始的id还要大,不是总体有序的。

二 优化

以下为shardingjdbc官网的解释:http://shardingjdbc.io/document/legacy/2.x/cn/02-guide/subquery/

查询偏移量过大的分页会导致数据库获取数据性能低下,以MySQL为例:

上面的SQL会使得MySQL在无法利用索引的情况下跳过1000000条记录后,再获取10条记录,其性能可想而知。而在分库分表的情况下(假设分为2个库),为了保证数据的正确性,SQL会改写为:

SELECT * FROM t_order ORDERBYidLIMIT0, 1000010

即将偏移量前的记录全部取出,并仅获取排序后的最后10条记录。这会在数据库本身就执行很慢的情况下,进一步加剧性能瓶颈。因为原SQL仅需要传输10条记录至客户端,而改写之后的SQL则会传输1000010*2的记录至客户端。

Sharding-JDBC进行了2个方面的优化。

首先,Sharding-JDBC采用流式处理 + 归并排序的方式来避免内存的过量占用。Sharding-JDBC的SQL改写,不可避免的占用了额外的带宽,但并不会导致内存暴涨。 与直觉不同,大多数人认为Sharding-JDBC会将1000010*2记录全部加载至内存,进而占用大量内存而导致内存溢出。 但由于每个结果集的记录是有序的,因此Sharding-JDBC每次比较仅获取各个分片的当前结果集记录,驻留在内存中的记录仅为当前路由到的分片的结果集的当前游标指向而已。 对于本身即有序的待排序对象,归并排序的时间复杂度仅为O(n),性能损耗很小。

其次,Sharding-JDBC对仅落至单分片的查询进行进一步优化。落至单分片查询的请求并不需要改写SQL也可以保证记录的正确性,因此在此种情况下,Sharding-JDBC并未进行SQL改写,从而达到节省带宽的目的。

更好的分页解决方案

由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案:

SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id

或通过记录上次查询结果的最后一条记录的ID进行下一页的查询:

SELECT * FROM t_order WHERE id > 100000 LIMIT 10

参照上面的优化方案修改后。

排序测试:

10
318410372342091846:2018-5-28 15:29:09
318410639322124402:2018-5-28 15:30:12
318410988967694355:2018-5-28 15:31:36
318410991522025492:2018-5-28 15:31:36
318410991714963464:2018-5-28 15:31:36
318410991983398922:2018-5-28 15:31:36
318410992222474332:2018-5-28 15:31:36
318410992398635107:2018-5-28 15:31:36
318410992541241414:2018-5-28 15:31:36
318410992679653457:2018-5-28 15:31:36
10
318410992960671822:2018-5-28 15:31:37
318410993120055362:2018-5-28 15:31:37
318410993254273071:2018-5-28 15:31:37
318410993443016819:2018-5-28 15:31:37
318410993577234441:2018-5-28 15:31:37
318410993740812315:2018-5-28 15:31:37
318410993904390269:2018-5-28 15:31:37
318410994101522480:2018-5-28 15:31:37
318410994340597868:2018-5-28 15:31:37
318410994479009916:2018-5-28 15:31:37
10
318410994604839011:2018-5-28 15:31:37
318410994734862347:2018-5-28 15:31:37
318410994869080185:2018-5-28 15:31:37
318410994999103514:2018-5-28 15:31:37
318410995137515522:2018-5-28 15:31:37
318410995275927606:2018-5-28 15:31:37
318410995405951099:2018-5-28 15:31:37
318410995540168730:2018-5-28 15:31:37
318410995670192178:2018-5-28 15:31:37
318410995804409857:2018-5-28 15:31:37
InitTest init...test

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: