跨库分页
背景
最近有个开发需求,是跨库分页,并且两个库的表结构不是一样的,一个是 mysql 的关系型结构,另一个是 dataware 的宽表数据。初步搜索了一下,暂时没有发现有中间件或者框架支持这种查询,但是中间搜索到有些思路值得记录,并且目前的做法是基于代码在内存中做分页返回。
条件
假设 mysql 中有三张表,结构是:
**student**
| name |
| --- |
| id |
| stu_name |
**course**
| name |
| --- |
| id |
| course_name |
**student_course**
| name |
| --- |
| id |
| stu_id |
| course_id |
另外,dataware 中的表结构是:
**student_course**
| name |
| --- |
| id |
| stu_id |
| stu_name |
| course_id |
| course_name |
需求
将两个不同数据库中的数据,查询啊、学生信息及学生包含的所有选课信息进行分页,例如展示为:
stu_id=1,stu_name=小明,course_id=1,course_name=科学
stu_id=1,stu_name=小明,course_id=2,course_name=数学
stu_id=2,stu_name=小美,course_id=1,course_name=科学
stu_id=2,stu_name=小美,course_id=3,course_name=英语
......
分析
如果是单库单表,可以直接使用现有的框架,例如 mybatis-plus 的分页插件进行分页,或者自行实现拦截器,在 SQL 中根据分页参数,拼接 limit 进行查询。
如果是单库多表,可以考虑将两个表的查询写在一个 SQL 中,用 union 联结查询结果,然后对总的查询结果进行分页,同样的可以使用 mybatis-plus 插件或者自行实现。
如果是多库多表,也就是跨库查询分页,则需要将两个库的查询结果进行联合分页,如何做?是个问题。
方案
方案 1
根据查询条件在每个库中都查询出符合条件的数据,然后将数据组装在一起,对组装的结果集,使用参数中的页码、页大小进行分页、筛选数据,相当于是内存分页。
此方案实现的难度最低,逻辑最简单,但是弊端也很明显,因为是基于内存做的分页,而且查询每次都需要将符合条件的所有数据查询出来,当查询到第 100 页的时候,按理说只需要第 100 页的数据,但是按照此方案的思路,需要将 100 页之前的数据也全部查询出来,甚至 100 页之后的所有数据也需要查询出来。
方案 2
假设结果需要按照数据创建时间排序,页面大小为 10,第一次查询为第一页,相当于单表的 limit 0,10,此时对每个数据库都根据条件查询、且根据时间排序,选择 limit0,10 的数据,然后将最多 20 条的数据聚合起来,进行内存分页。
当查询第二页时,相当于单表的 offset 10 limit 10,此时需要分页参数以外,还需要传入上一次查询获得的最大时间,此时则在每个数据库中分别查询数据时间>=传入时间的数据,且带上限制条件 limit 0,10(每个数据库都查询 10 条),因为有可能有极端情况:当前查询的页数据都只存在于一个库中,然后再将两个数据库的查询结果联结分页(基于内存)。
此种方案的限制条件很明显,就是在查询除了第一页之外的操作时,都需要传入时间(排序基准值),有此种限制存在时,则不能允许跳页的情况存在,因为随便选取一页并不能知道排序基准值是多少,否则又会回到方案 1 的情况,将所有数据查询出来在内存中做分页。
其他方案
待想到或有其他好的建议时再补充......
版权声明: 本文为 InfoQ 作者【zarmnosaj】的原创文章。
原文链接:【http://xie.infoq.cn/article/df46d49fd7304ddb10d392af8】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论