背景
分库分表一般都会按照hashKey进行路由库表,一般唯一确认库表。在生产环境中一般以userId作为hashKey进行分库分表。例如用户订单表,以userId为hashKey,将该用户的所有订单都路由到一个库表,这样很方便在用户端进行分页查询所有订单。
但是对于商家来说,如果要看所有客户的订单信息,还要分页,怎么办?
一般情况下,在创建用户订单的时候,会同步写用户的订单表,然后异步写商家的订单表。这样也很方便做商家分库分表的分页查询。
本文讨论的问题聚焦在:如果不异步写商家订单表,那么怎么分页查询?
分析
这个问题的难点在什么地方?
如果不分库分表,那么直接查询limit就可以了。如select * from order where seller_id = 666 order by create_time limit 10, 10,从订单表中查找卖家id为666,跳过前10条数据,再取10条。
在分库分表场景下,如果在每个分表下都执行上面的语句,然后对所有结果排序后返回,那么数据可能是不对的。举个极端的例子说明下:如果前10条数据都在order_0,那么在执行上面的语句的时候,order_1的前10条数据就会缺失,导致数据不对。
方案一
定义每页的数据量为pageSize,定义页码为pageNum(假设从第0页开始)。那么分页查询的语句为limit pageNum*pageSize, pageSize。简单记为limit m,n。
每次的分页查询sql都改写为limit 0, m+n。然后每个分表的查询结果聚合、排序后,跳过m条取n条。
这样的核心问题在于:随着页码的加大,后面的分页从MySQL的每个分表拉取的数据量越来越大。本来做分库分表是为了提升性能,这么做后可能导致每个分表拉取的数据量都很大、都很慢。即使使用归并排序,如果数据量巨大,在内存排序也不是一个好主意。可能会进行磁盘的归并排序。这又涉及到了从MySQL每个分表数据查出来后,在本地落盘的问题。
参考实现:sharding-sphere。
方案二
方案一实现了需求,而且可以满足任意页的随机访问。如果不做任意页的随机访问呢?比如当前的移动端,都是下拉拉取新的页。业务场景是逐页访问。
取第一页的数据没有差别。例如每个表都是执行limit 0, 10。
参考
- # 【大厂面试题】分库分表下如何进行分页查询?
- 视频中思路很对,但存在算法错误。limit x/N, y不对,需要是limit x/N, x+y才行。
- 分库分表下如何进行分页查询?(上面视频的PPT)