分库分表后的分页查询

分库分表后的分页查询,第1张

如果要获取第N页的数据(每页S条数据),则将每一个子库的前N页( offset 0,limit N*S )的所有数据都先查出来(有筛选条件或排序规则的话都包含),然后将各个子库的结果合并起来之后,再做查询下 top S (可不用带上相同的筛选条件,但还要带上排序规则)即可得出最终结果,这种方式类似es分页的逻辑。

如果要获取第N页的数据,第一页时,是和全局视野法一致,但第二页开始后,需要在每一个子库查询时,加上可以排除上一页的过滤条件(如按时间排序时,获取上一页的最大时间后,需要加上 time >${maxTime_lastPage} 的条件;如果没有排序规则,由于是默认主键id的排序规则,也可加上 id >${maxId_lastPage} 的条件),然后再 limit S ,即可获取各个子库的结果,之后再合并后 top S 即可得到最终结果。在类似app中列表下拉的场景中,业务上可以禁止跳页查询,此时可以使用这种方式。

在大数据量的前提下,需要查询的数据,从概率论角度,是均匀分布在各个字库中的,因此可以假定需要查询的第N页数据,在子库中都处于第 N/X 页的前 S/X 条中(X=子库数);所以查询子库时,限定 offset ((N/X)-1)*S/X,limit S/X 即可,例 N=S=100,X=2 时,子库分页条件为 offset 4950,limit 50 ;然后合并子库结果后即可得出最终结果,当然这个结果是不准确的。在类似网页回帖上的场景下,往往数据精度要求不太高,此时可以使用这种方式。

也是在大数据量的前提下,依据概率论,可以假定需要查询的第N页的数据,在子库中都处于第 N/X 页的后面。然后可按如下步骤查询:

1). [第一次查询] 按指定条件(筛选条件或排序规则条件)查询各个子库的S条数据,即 offset ((N/X)-1)*S/X,limit S

2). 如果没有排序规则条件,则默认主键id排序,那么获取各个子库的返回数据的最小值和最大值: min_i_id,max_i_id ;如果有排序条件,就按排序条件获取

3). 比较各个子库的 min_i_id ,得到最小的,定义为 min_id

4). [第二次查询] 再次查询(有筛选条件的话也要包含)各个子库,加上条件: min_id<id<max_i_id ;(注: min_i_id = min_id 的子库可省略查询)

5). 查看第二次查询结果中, min_id_id != min_id 的其它子库中,共多了几条数据,如果多了M条,则可以得出全局中,min_id前面的数据有 (((N/X)-1)*S/X)*X - M => ((N/X)-1)*S-M 条, ((N/X)-1)*S-M 即为 min_id 的全局offset

6). 计算真正的全局offset: ((N-1)*S) 和 min_id 的全局offset: ((N/X)-1)*S-M 之间的差值K,由公式可得: K>=0

7). 合并第二次查询的各子库结果,并按id排序后,以 K为offset,S为limit 即可得到最终全局的分页结果

参考: https://mp.weixin.qq.com/s/H_2hyEqQ70Y_OoFZh_P_5A

mysql分库分表一般有如下场景

其中1,2相对较容易实现,本文重点讲讲水平拆表和水平拆库,以及基于mybatis插件方式实现水平拆分方案落地。

在 《聊一聊扩展字段设计》 一文中有讲解到基于KV水平存储扩展字段方案,这就是非常典型的可以水平分表的场景。主表和kv表是一对N关系,随着主表数据量增长,KV表最大N倍线性增长。

这里我们以分KV表水平拆分为场景

对于kv扩展字段查询,只会根据id + key 或者 id 为条件的方式查询,所以这里我们可以按照id 分片即可

分512张表(实际场景具体分多少表还得根据字段增加的频次而定)

分表后表名为kv_000 ~ kv_511

id % 512 = 1 .... 分到 kv_001,

id % 512 = 2 .... 分到 kv_002

依次类推!

水平分表相对比较容易,后面会讲到基于mybatis插件实现方案

场景:以下我们基于博客文章表分库场景来分析

目标:

表结构如下(节选部分字段):

按照user_id sharding

假如分1024个库,按照user_id % 1024 hash

user_id % 1024 = 1 分到db_001库

user_id % 1024 = 2 分到db_002库

依次类推

目前是2个节点,假如后期达到瓶颈,我们可以增加至4个节点

最多可以增加只1024个节点,性能线性增长

对于水平分表/分库后,非shardingKey查询首先得考虑到

基于mybatis分库分表,一般常用的一种是基于spring AOP方式, 另外一种基于mybatis插件。其实两种方式思路差不多。

为了比较直观解决这个问题,我分别在Executor 和StatementHandler阶段2个拦截器

实现动态数据源获取接口

测试结果如下

由此可知,我们需要在Executor阶段 切换数据源

对于分库:

原始sql:

目标sql:

其中定义了三个注解

@useMaster 是否强制读主

@shardingBy 分片标识

@DB 定义逻辑表名 库名以及分片策略

1)编写entity

Insert

select

以上顺利实现mysql分库,同样的道理实现同时分库分表也很容易实现。

此插件具体实现方案已开源: https://github.com/bytearch/mybatis-sharding

目录如下:

mysql分库分表,首先得找到瓶颈在哪里(IO or CPU),是分库还是分表,分多少?不能为了分库分表而拆分。

原则上是尽量先垂直拆分 后 水平拆分。

以上基于mybatis插件分库分表是一种实现思路,还有很多不完善的地方,

例如:


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/sjk/6723336.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-27
下一篇 2023-03-27

发表评论

登录后才能评论

评论列表(0条)

保存