美文网首页MySQL
一种分库分表情况下的数据库分页查询算法(支持条件查询)

一种分库分表情况下的数据库分页查询算法(支持条件查询)

作者: 妖云小离 | 来源:发表于2019-03-18 19:36 被阅读359次

版权说明

未经作者允许,一律不得私自转载,否则将追究责任。

技术背景

  1. 随着业务数据量的增大,传统的关系型数据库(如mysql)的单表处理效率达到瓶颈。但是由于业务需要,此类数据仍需存储在关系型数据库中。为了提高效率,可以把数据按一定规则分类,分别存入n张表中。这n张表按一定规则分类,分别由m个数据库管理。如下图所示:


    分库分表架构图
  2. 传统单表的分页查询功能,需前后端配合实现。当需要查询某一页时,前端传给后端两个参数①页码 ②分页大小;后端接收这两个参数,并通过公式:
    起始行号 = (页码-1)*分页大小
    计算出起始行号,然后依托于LIMIT关键字,其所需参数为①起始行号分页大小,到数据库中进行分页查询。单次查询后即返回结果集,并同时返回一个参数——所有符合查询条件的数据的数量,该回参用于前端显示数据总量及页码的计算。
  3. 基于上述分库分表方案,由于把一张表拆分成了n张表,并且n张表分布在不同的m个数据库中,那么由此引发出了一个问题:传统的分页查询已经不再适用。由于分库分表,可能存在某一页的数据被存储在多张表中的情况,仅仅靠前端传来的两个参数无法进行传统的分页查询。

核心功能

  1. 在不变动前端的前提下,通过设计算法,动态计算LIMIT关键字所需的两个参数①起始行号 ②分页大小,使得关系型数据库分库分表情况下,可以像单表一样进行分页查询。
  2. 由于两个参数是动态计算的,当查询到了足够的数据(分页大小)之后,便返回结果。减少sql查询操作,提高效率。

算法描述

  1. 从第一张分表开始循环,直到满足结果或循环完最后一张分表
  2. 查询当前分表i,得到结果集resTemp
  3. 如果resTemp有数据,则把resTemp加入返回结果resList
    3.1 如果resList满足分页大小,则退出循环
    3.2 如果resList不满足分页大小,则下次查询起始行号startTemp=0(从表头0开始),且下次查询的分页大小pagesizeTemp -= resTemp的大小
  4. 如果resTemp无数据,则startTemp -= count(当前表数据量);

时间复杂度

假设分表数为n,则在最坏情况下,算法需要执行n次循环,每次循环执行一次select和count操作。故此算法的时间复杂度为O(n)。

举例说明

如图1所示,假设我们把mysql单表分成了3张分表,并且取名为表001,表002,表003,在这三张表中,分别存储了一些数据,我们把这些数据进行编号,一共十条,其中表001存储了7条,表002存储了2条,表003存储了1条。


图1. 单表分成了3张分表

算法的核心思路为:无论分了多少张表,都把它们看作是一张逻辑总表,这张表由所有分表首尾相连。如下图2所示:


图2. 逻辑总表
现在假设前端用户需要分页查询,从第1页开始查,每一页显示3条数据,直到查询完所有数据。那么转换为SQL就如图3所示:
图3. 逻辑总表的分页查询

分页查询依托于LIMIT关键字,其所需参数为①起始行号 ②分页大小。基于逻辑总表的分页查询,我们可以得到如下对应关系:

Sql语句 页码 涉及的表 查询的数据
LIMIT(0,3) 1 表001 1,2,3
LIMIT(3,3) 2 表001 4,5,6
LIMIT(6,3) 3 表001,表002 7,……,8,9
LIMIT(9,3) 4 表001,表002,表003 ……,10

下面我们对于四条sql语句逐一分析:
LIMIT(0,3):
表001作为起始表,对其执行LIMIT(0,3)可返回1,2,3这3条数据,满足分页大小,即可返回查询结果:1,2,3。
LIMIT(3,3):
表001作为起始表,对其执行LIMIT(3,3)可返回4,5,6这3条数据,满足分页大小,即可返回查询结果:4,5,6。
至此,我们的查询只涉及表001。上文提到,由于分库分表,可能存在某一页的数据被存储在多张表中的情况,下面我们来看这种情况。
*LIMIT(6,3):
表001作为起始表,对其执行LIMIT(6,3)可返回7这1条数据,还需2条;由于对表001的查询有满足的数据,所以对表002执行LIMIT(0,2)。为什么是LIMIT(0,2)?“0”是因为对表001的查询有满足的数据,所以对表002只需从第一条开始查询即可。“2”是因为此次查询还需要2条数据即可返回,我们把这种规则称为 规则1。表002执行完LIMIT(0,2)可返回8,9这2条数据。至此,满足分页大小,即可返回查询结果:7,8,9。
*LIMIT(9,3):
表001作为起始表,对其执行LIMIT(9,3)无数据返回;由于对表001的查询无数据返回,所以对表002执行LIMIT(2,3)。为什么对表002执行LIMIT(2,3)?“2”是因为对表001的查询无数据返回,所以对表002的起始位置计算公式为:9-count(表001) = 2,
其中count(表001)是对表001的数据进行count计数操作。“3”是因为还需要3条数据即可返回,我们把这种规则称为 规则2。表002执行LIMIT(2,3)后仍无数据返回(注:mysql行号下标从0开始计数)。那么同理,根据 规则2,继续对表003执行LIMIT(0,3)可返回10这1条数据。若还有表004,则仍可根据规则1计算下去。但是由于已经遍历完了全部3张分表,此次查询返回结果:10。

根据算法,我们进一步抽象出如下逻辑:
变量说明:
resTemp——当前分表查询结果集
startTemp——下次查询的起始行号;
pagesizeTemp——下次查询的分页大小
resList——总的返回结果

伪代码逻辑:

for 循环所有的分表:
    查询当前的分表得到结果集resTemp;
    if resTemp有数据:
        then
        把resTemp加入返回结果resList;
        if resList满足分页大小:
            then break;
        else resList不满足分页大小:
            startTemp= 0;
            pagesizeTemp -= resTemp.size();
        end if
    else resTemp没有数据:
        startTemp -= count(当前表数据);
    end if
end for

注:由于数据量在实际情况当中是不断变化的,必须保证startTemp>=0 && pagesizeTemp>0
所以for循环继续下一次查询需满足的条件有:

  1. 尚未循环完最后一张分表
  2. startTemp>=0
  3. pagesizeTemp>0
  4. 已查询到的数据量尚未满足分页大小

Java实现

接口

/**
 * 分库分表下的分页查询参数接口
 */
public interface MultiTableQuery {
    /**
     * 需要实现 单表的分页查询 功能
     * @param shard 表号
     * @param start 起始行号
     * @param pageSize 分页大小
     * @param queryParam where查询条件
     * @return 单表查询数据集
     */
    List<Map<String, Object>> queryByPageAndShard(int shard,int start, int pageSize,Map<String,Object> queryParam);

    /**
     * 需实现 单表count 功能
     * @param shard 表号
     * @param queryParam where查询条件
     * @return 单表中数据量
     */
    int countByShard(int shard,Map<String,Object> queryParam);

    /**
     * 需返回 分表总数
     * @return 分表的总数,即共有多少张表
     */
    int getShardNum();
}

工具类

/**
 * 分库分表工具类
 */
public class MultiTableUtils {

    /**
     * 分库分表下的分页查询
     * @param m 需要实现MultiTableQuery接口
     * @param start 起始行号
     * @param pageSize 分页大小
     * @param queryParam where查询条件
     * @return 查询数据集
     */
    public static List<Map<String, Object>> queryAllByPage(MultiTableQuery m,int start,int pageSize,Map<String,Object> queryParam) {

        int startTemp = start;
        int pageSizeTemp = pageSize;
        List<Map<String, Object>> resPendingList = new ArrayList<>();
        for (int i = 0; (i < m.getShardNum()) && (startTemp>=0) && (pageSizeTemp>0); i++) {
            List<Map<String, Object>> tempList = m.queryByPageAndShard(i,startTemp, pageSizeTemp,queryParam);
            if (CollectionUtils.isEmpty(tempList) ) {
                //如果此次分页查询没有数据,且不是从表头开始查
                if (startTemp > 0) {
                    int num = m.countByShard(i,queryParam);
                    //计算下一次查询的起始位置
                    startTemp = num > 0?startTemp-num:startTemp;
                }
            } else {
                //如果此次分页查询有数据,且还没有达到分页总数
                if (resPendingList.size() + tempList.size() < pageSize) {
                    //计算下一次查询的   剩余分页的数量
                    pageSizeTemp -= tempList.size();
                    //因为此次分页区间有数据,那下一次查询直接从表头查询即可
                    startTemp = 0;
                    resPendingList.addAll(tempList);
                } else {
                    resPendingList.addAll(tempList);
                    break;
                }
            }
        }
        return resPendingList;
    }
}

用法:

在service层实现MultiTableQuery 接口,然后直接使用工具类传入该service实例即可。其他参数注释都有说明。

相关文章

  • Day13 MyCat+ShardingJDBC+ES略提

    为什么要分库分表? 分页查询? 分库分表下的分页查询[https://www.jianshu.com/p/c1e8...

  • 一种分库分表情况下的数据库分页查询算法(支持条件查询)

    版权说明 未经作者允许,一律不得私自转载,否则将追究责任。 技术背景 随着业务数据量的增大,传统的关系型数据库(如...

  • 像查询DB一样查询redis

    设计目的:希望查询redis缓存像查询数据库一样,支持多条件组合查询、模糊查询、区间查询、多字段排序查询、分页查询...

  • sql优化

    1. 大量数据的分页查询 有一张财务流水表,未分库分表,目前的数据量为9555695,分页查询使用到了limit,...

  • 分库分表时分页查询

    分页的时候,先到逻辑主表中查询出最终分页后的主键id们,然后根据id取模组合出对应的分表名称,再通过id在这张表中...

  • 分库分表

    数据库分表可以解决单表海量数据的查询性能问题,分库可以解决单台数据库的并发访问压力问题。 分库分表目前有很多的中间...

  • hibernate中的查询

    HQL 查询所有 条件查询 分页查询 Criteria 查询所有 条件查询 分页查询 查询总记录 原生SQL

  • SQL性能优化

    数据库设计 分库 分表 创建别名访问 创建不同的数据文件 读写库分离 alwayson 查询语句优化 索引 子查询...

  • NoSQL

    关系型数据库: 超过500万行,30列,需要分表分库。 MongoDB优势: 1、完全的索引支持,适合实时查询。2...

  • MyCat 启蒙:分布式系统的数据库架构演变

    MyCat 是一个数据库分库分表中间件,使用 MyCat 可以非常方便地实现数据库的分库分表查询,并且减少项目中的...

网友评论

    本文标题:一种分库分表情况下的数据库分页查询算法(支持条件查询)

    本文链接:https://www.haomeiwen.com/subject/mrgbmqtx.html