ShardingJDBC 数据库分片 流式处理+归并排序 优化原理刨析

1. 分库分表下的分页查询

业务数据达到一定数据量时,必定会引入数据库分片,但当对于分片的情况下,分页查询是如何做到的?

比如: 数据库db1,中有三个user表,user_0,user_1,user_2,三个表的分片策略是以userId 与 3 取余。分片配置入下

# 指定user表的数据分布情况
spring.shardingsphere.sharding.tables.user.actual-data-nodes = db1.user_$->{0..2}

# 指定user表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column = id
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression = user_$->{id % 3}

现在执行分页查询语句

select * from `user` order by id limit 10000,10;

考虑到三个表的分片策略,如果要正确获取到 10000-100010 区间的数据的话,需要将三个表都查询出100010 条数据,再做归并排序,最后才能得到正确的结果集。

这样便产生了最让人担忧的问题,如果没张表都要查询那么多数据在我内存中排序,如果我有上百张表,那内存迟早溢出,产生OOM?

2. ShardingJDBC 的优化

官方文档在这里:shardingsphere

ShardingSphere 对分片查询的优化是通过使用 流式归并+优先级队列 实现的,可以减少对本地内存的占用(只有每个数据源的游标而已),但是并不能减少带宽资源的消耗。

ShardingJDBC 对这种分片查询的处理方式为:

  1. 从各个数据节点获取对应的数据集。
  2. 将上述获取的数据集,进行组合、归并, 最后得到一个符合预期的数据集。
  3. 将正确的数据集返回。

上述三个步骤中最重要的就是 归并的策略…

ShardingJDBC 的归并由归并引擎负责,归并引擎提供了三种归并方式
流式归并、内存归并和装饰者归并

  • 流式归并是指每一次从结果集中获取到的数据,都能够通过逐条获取的方式返回正确的单条数据,它与数据库原生的返回结果集的方式最为契合。遍历、排序以及流式分组都属于流式归并的一种。

  • 内存归并则是需要将结果集的所有数据都遍历并存储在内存中,再通过统一的分组、排序以及聚合等计算之后,再将其封装成为逐条访问的数据结果集返回。

  • 装饰者归并是对所有的结果集归并进行统一的功能增强,目前装饰者归并有分页归并和聚合归并这2种类型。

因为流式归并是从数据库中返回的结果集是逐条返回的,并不需要将所有的数据一次性加载至内存中,因此,在进行结果归并时,沿用数据库返回结果集的方式进行归并,能够极大减少内存的消耗,是归并方式的优先选择。

3. 流式归并的原理

ShardingJDBC 的流式处理和JDBC的ResultSet的原理是一样的,主要是通过和数据库保持长连接,每次next都只取当前游标所在位置的一条数据,然后在内存中进行归并。

具体流程如下:

假设user表分为 db0: user_0, db1: user_1, db2: user_2 三张表
查询语句为:
select * from user order by id limit 10,10;

  1. 因不确定数据分布关系,所以,下发到三个表的sql指令都为 select * from user_$ order by id limit 0,20;

在这里插入图片描述

  1. 数据源执行了sql后,并不会将查询到的数据集直接返回给客户端,而是先将结果集存储在数据源本地,等待client通过游标一条条读取。每一个表都会维护一个自己表的游标,初始位置为第一条记录。
    在这里插入图片描述

  2. 每一轮都只传输游标当前指向的记录,client会将接收到的记录加入优先级队列,第一轮的时候client 维护的优先级队列如下所示。优先级队列是按照sql要求的排序字段排序。
    在这里插入图片描述

  3. 优先级队列队首出队,会执行next,去对应的db中取下一条记录,此时数据源维护的游标要向下移动一格。上述例子中,便会去user_1 中取出下一条记录,再重新入队进行排序,第二轮的结果如下图所示。

在这里插入图片描述

  1. 优先级队列操作next的同时,内部维护了一个 rowNumber,用来表示当前记录是第几个,每次取next时,都会 +1 ,源码部分如下:
    在这里插入图片描述
    limit分页的时候,便是通过这个字段找到对应开始的记录,开始拼接有效的结果集。
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值