java分布式架构-分库分表详解

java分布式架构-分库分表详解

既然是“漫谈分库分表”,那么我们需要确定我们要谈什么,不谈什么。

首先,我们不讨论具体的分库分表框架的实现和源码,这不是我们讨论的范围。
我们讨论的是思路,主要讨论如何分库分表的套路,有什么坑,有什么心得,不针对具体的细节进行展开式讨论。当然我自己的能力有限,只是希望能够抛砖引玉。
我们要明确,分库分表,并不是一个银弹,它只是我们针对MySQL单机性能不够的情况下,想要节约成本的一种方式。对于boss来说,既想要想要节约成本,又想要支撑业务,提供稳定持久度性能。
程序员发挥出聪明才智,绞尽脑汁,日复一日的努力与实践,最终产生出主要的两种方式:

agent嵌入式模式,用一个jar包,集成到我们的代码里,在代码里通过路由规则,分片键方式进行分库分表,属于嵌入式方式。
cs模式(客户端-服务端模式),提供一个三方组件, 如: mycat,sharding-sphere中的proxy方式,类似于mycat;存在中心化,需要保证三方组件的高可用。
如果有更好的技术选型,我们宁愿不用分库分表,因为它本身就是一个复杂的解决方案。只是一种折中,更合适的是NewSQL、商业化数据库(比方说,Oracle,在大部分场景下,性能足够用,但是费用高昂)。

如果真的有一天,出现了一个优秀的、经济的newSQL, 比方说 oceanbase,tidb,那么我们基本上可以告别分库分表。

我们之所以选择使用分库分表策略,根本上还是因为,一方面是因为我们的使用成本不能太高;一方面,单机DB数据库性能不够了;一方面,newSQL当前还不成熟,太贵,不敢用。

分库分表用的厂家挺多的,有丰富的开源框架,有社区,还有成熟的案例,所以我们采用,

直接原因在于,阿里站台了,我们国内的风气是,阿里用啥我用啥,阿里怎么做我这么 跟风严重。我的想法是,我们还是有自己的技术前瞻性一些看法,最好不要唯阿里,唯技术。

说了这么多,我们回归正题,开始看问题。

  1. 只做分表可以吗?还是必须要分表又分库,如果是分库的话 库是在多个服务器上吗?这个怎么来考虑
    我想说,还是要看业务规模,既看当前的业务规模,也看未来3-5年的业务发展趋势。

涉及到技术选型,我们的宗旨是,永远选择最适合当下业务的、成本最低的,收益最高的,合适的就是最好的。

我们选择的方案最好是技术团队刚刚好能够hold住的选型。如果选型已经不适合当前的业务发展,那么大可以换套更适合的。这个本来就是事物发展的必然规律。

要么,业务还没发展到一个更高层次,就已经GG了,那么刚刚好,不用浪费钱买更好的设施,刚好止损;

要么,就是当前的方案确实不够用了,我们换了一套更牛X的,虽然说这样会花更多的钱,请更多的人,但是,这不正合我们的心意么,我们的目的本身就是通过合适的技术架构,更加优秀的代码,支持业务发展。

一句话总结就是,既然不得不花钱,那该花就花吧。

分场景讨论
一图胜千言,我们分别看看这两种场景。

对于 离线数据分析场景

只分表是够的,因为你主要用来分析数据,分析数据完成之后的数据就可以删掉了。异步任务删掉若干月/天的数据。

对于 实时业务系统

如果是一个分布式的业务系统 2C,需要承载巨量的流量的,建议 分库分表 同时考虑。

分库分库的前提,预估业务量
分库分表的前提,是预估业务量,我们提供一个经验值,不代表最合适的,只是一个定性的分析:

QPS 500-1000以下, 那么采用主从读写分离,基本上足够支撑业务了;

QPS 1000-10000,考虑分布分表是一个比较合适的事情

12000TPS 30000QPS 32库 1024表

1000多万 16000QPS 16库512表
本质上来说:分库分表是一锤子买卖,前期的设计很重要,决定了后期扩容以及数据迁移的难度。在前期设计的时候,大概率我们需要做好未来3-5年的规划,短的话需要做1-2年的规划,根据规划来确定是不是要分库分表,以及分多少库、多少表。

回到问题本身,这个主要取决于当前的业务量,以及业务量的增速。

我们根据这几个维度,给出一组公式:

某年数据增量M = (1 + 数据年增速K)^ n * 初始数据量 N

第一年增量 M1 = (1+k) * N

第二年增量 M2 = (1+K)^2 * N

第三年增量 M3 = (1+K)^3 * N

三年数据总量 M’ = N + m1 + m2 + m3
我们就以单表承载1000万数据来算,一共要有几张表,当前不一定是1000w,2000w-5000w都可以,这个首先是一个经验值,其次还需要定量分析。

定量的分析,就需要进行压测。我们需要针对你的线上的配置,用一个库的实例去压测,压出你的这个配置下,在不影响系统吞吐量的前提下,单表的最大容量,压测是一个稳妥的环节,能够在前期很好的指导我们进行设计。

我们接着讨论,什么时候需要分库,必须要保证每个数据库都是一个独立的实例么?
并不是,我们还是要具体问题具体分析。

如果是开发环境,也就是研发RD自己写代码用的库,那么多套库在一台机器上也可以,毕竟开发环境没有并发量,最多拿来开发,只要不用来压测就没啥问题。

如果是线上环境,除了要将库部署到多台机器,还得考虑读写分离,以及库的高可用。线上线下的主要区别在于,线上有高可用的要求,而线下不需要。

思考一下,两者区别是什么,区别就在于成本的控制。

我们给出结论,具体什么时候要把数据库部署到一台机器实例,还是要看场景,看成本,看自己需不需要。具体问题具体分析。

  1. 路由键怎么生成?用雪花算法可以吗?如果原来的数据库主键是自增的,没有业务唯一约束,如果迁移之后,原先的数据怎么在分库分表中进行路由
    好问题。

首先说,路由键怎么生成?

本质上,这是一个如何实现一个可靠的分布式发号器的问题。我们只说思路,因为展开说都能但单独说好半天了。

思路:

对于某些框架而言,他们有自己的主键生成器,比如说shardingSphere/ShardingJDBC 类SnowFlake算法;

UUID:字符串形式,确实是唯一,但是可读性差,不好做数学计算,不直观,比较长,占用空间大
SNOWFLAKE:可以用,也可以用改进leaf,leaf本身就是一套完善的分布式发号器,自己也有高可用保障。
当然还有别的方式:

因为既然已经做了分库分表,大概率你的系统也是分布式的吧,那么用进程内的发号不是一个理想的方式。

如果要简单实现一种分布式发号服务,我们可以利用 redis increment 实现一套发号器,也可以借助数据库的自增唯一id来做,但是我们还是需要自己进行开发,实现一个发号系统。

简单的上一个图,表达一下思路,这块儿内容之后会单独写文章来讲。

总结一下就是,本质上,这是一个如何实现一个可靠的分布式发号器的问题。

所以得依赖某个具体的分布式发号机制 这个问题不用纠结,关注一下最终的选型就好,多进行权衡。

  1. 如果本身是一个单库,并且没有路由键,完全拿主键当唯一标识了,我分库分表怎么玩?
    很简单,你原来的唯一标识是什么,分库分表之后还用这个就行了。

但是,因为本身没有一个业务属性的键,所以建议在进行数据迁移之后,加入一个业务属性的自然主键,并且大概率你需要配置一下新的路由规则。

具体的过程为:

迁移数据
更改路由配置 指定一个新的查询规则,分库分表的路由规则
改代码,把代码中涉及到C R U D 的代码,比如说DAO、repository中包含的代码,代码都加上路由规则,简单的说你还是可以用原来的id去执行查询 、插入 、删除的,但是主要的改动点就在于你需要有一个路由规则。
我们说,数据库迁移到分不分表的核心:是保证数据的完整性,代码该重构就重构,很难有一个全面的不需要改代码的方案,我们只能折中权衡,降低复杂度。

原先的主键id,迁移到分库分表新库中,已经不是连续的了,但是还需要保证unique,新的数据库表中的自增主键还需要有,但是没有业务属性了,之所以分库分表之后还需要有自增主键,主要在于提升插入效率,查询效率。通过主键索引树,进行回表操作。

相当于你原先用了自增id是有业务属性的,这里说句题外话,请尽量不要使用自增主键代表业务含义。

  1. 分片键怎么选择
    我们的答案依旧不能给出一个准确的说法,我只能说,要根据业务场景的要求去选择。

这么说太笼统了,我们通过几个例子来表达一下。

对于用户表,使用用户唯一标识, 如:userId作为分片键;

对于账户表,使用账户唯一标识,如:accountId作为分片键;

对于订单表,使用订单唯一标识, 如:orderId作为分片键;

对于商家相关信息表,使用商家唯一标识, 如:merchantId作为分片键;


如果我们要查一个用户的订单,那么我们应该用userId去路由表,插入订单到订单表,保证一个用户的所有订单都能够分布在一个表分片上。这样做能够很好的避免引入分布式事务。

如果说,维度不是用户,而是其他维度,比方说,我们想查询某个商家的所有用户的订单,

那么我们就应该用商家的merchantId也去存一份数据,路由的时候用商家id去路由,只要是这个商家的用户订单,我们写入到商家的订单表里,那么对于商家所属的订单,我们就可以从某个分片上获取到。

用一个图表达,能够很明确地体现上述的说明内容:

对于用户而言,分片键作用方式如下图:
usertable.png

对于商家而言,分片键作用方式如下图:
merchanttable.png

所以我们的结论就是:要根据业务场景的要求去选择,具体问题具体分析,尽量保证不引入分布式事务,提升查询效率。

补充一句,对于主流的做法,如果需要有复杂查询,要么依据不同维度去进行双写,要么直接通过引入异构的方式去查询,比方说使用elastic search,或者使用hive等方式。

4.批量插入数据的时候,会往各个分库去插,在实际业务中是否要做分布式事务
第三个问题或多或少也提到了这个问题的答案。

我们在落地分库分表的过程中,要尽量避免引入分布式事务。

因为从上面第三个问题,你会发现,如果我们有路由键,问题就简单的多了,我们大部分情况下不需要引入分布式事务,但是如果没有就很痛苦。

对于乱序插入且需要保证插入事务性的场景,就需要分布式事务。但是这样做效率太低,也不合适。

首先乱序插入的场景并不多,其次如果引入分布式事务,那么事务的力度也不小,而且对于插入的性能有着显著的影响。不是最佳的方式。

我的建议就是,还是基于最终一致性去做,否则引入分布式事务,太影响效率了,而且也会增加系统的复杂度,我觉得我们设计系统的宗旨就是,能不用复杂的方案就不用,有时间喝喝茶,干点别的何乐而不为呢。

所以这个问题的结论就是:尽量避免分布式事务,如果不得不引入,需要尽量缩小事务的范围和力度。通过折中,多去考虑一下方案的可行性,
性能很重要,没有分布式事务也能做,怎么做,就是通过最终一致性。

但是,如果你说 “我就是避免不了分布式事务啊,那咋办嘛”。那就用吧,若无必要,勿增实体。不得不用,就用,没什么好说的。

5.如果一个库有很多张表,对一张表进行分库分表了,此时不分库不分表的表怎么放置, 是否指定到分库里某一个库里面?
本质上:这是非分库分表的数据与分库分表数据的分布的一个问题。

实际上,分库分表中间件往往都有对应功能,这个功能往往叫做默认路由规则,怎么理解呢?

就是说,对于没有分库分表的这些表,走默认路由规则 就行了,这样的话始终会路由到default DataSource上去。

相当于是一个白名单。找一下中间件的文档,看看默认路由规则怎么配,基本上中间件都考虑这个问题了,对于ShardingSphere而言,一个配置样例如下:

CustomerNoShardingDBAlgorithm

default-table-strategy: (缺省表分区策略)

complex:

sharding-columns: db_sharding_id

algorithm-class-name: com.xxx.XxxClass

tables:

ops_account_info: (必须要配置这个,才能使用缺省分表策略)

actual-data-nodes: db-001.ops_account_info
详细的举个例子,比方说:

一个服务在原有的数据库进行分库(比如user库分为了user01,user02)的时候,是把不分表的表强制路由走某一个数据库吗(比如把不分表的表都路由到user01)?

这里说到的本质就是: 默认路由规则,我们只需要配置某些表走默认路由规则就行了,比方说,我们现在有user 表 order表,config表,其中user表、 order分库分表,而config没有分库分表。

那么我们只需要把config表放在user库的0库,1库,2库,随便某个位置,

放好之后,我们只需要在分库分表中间件的配置文件中配置默认路由规则,把config表特殊配置一下,只要查config表,就走到这个指定的库上去。

其他的也类似 ,只要有这种需求,就增加对应的配置。

一定要显式告诉中间件,哪些表不走路由规则,并且要告诉它,这些表具体放在哪儿,
最好是放在请求量不大的库里,或者说单独搞一个库也可以,这个库放的都是不进行分库分表的表,
并配置不走路由规则就完事儿了,其实还是默认路由规则。

为什么这么做呢?有什么意图呢?

我的理解就是:之所以我们分库分表的原因,就是因为请求很大需要降低并发度;而对于请求频率小的表,我们可以不分库分表还是通过单表方式使用,那么就可以配置为默认路由规则就好。

8.数据迁移流程以及如何保证数据一致性
简单的概括,数据迁移依赖于数据的双写;数据一致性,依赖于数据完整性校验。

对于迁移而言,我们有以下步骤:

先修改代码,加入双写分库分表代码;进行上线
开始进行数据双写,同步增量数据 ;双写,主要目的是追上实时数据,给全量同步数据一个deadline,保证从这个时间之后的数据都是完整的(同时,通过异步数据完整性校验程序去校验数据完整性,但是如果我们能够保证双写可靠性,这个对比可做可不做。最好还是做一下)
全量历史数据同步,并校验数据完整性;一般全量数据同步,不用同步写的方式,原因在于同步写入一方面代码耦合度高,一方面是对系统有影响。所以我们往往通过异步方式进行写入,这个过程后文有图进行说明;
去掉双写代码,将查询分库分表的逻辑全量;
通过开关切换,在全量数据同步完成之后切换到全量读写分库分表逻辑即可。此时老的逻辑已经没有请求路由过去了,我们只需要找个发版窗口把老逻辑下线就可以,此时线上已经完全迁移到分库分表的代码流程。
最后我想说,一定要回归,一定要回归,一定要回归!!!

1 传统项目结构

2 数据库性能瓶颈
① 数据库连接 数据库连接是非常稀少的资源,MySQL数据库默认100个连接,单机最大1500连接。如果一个库里既有用户相关的数据又有商品、订单相关的数据,当海量用户同时操作时,数据库连接就很可能成为瓶颈。

② 数据量 MySQL单库数据量在5000万以内性能比较好,超过阈值后性能会随着数据量的增大而变弱。MySQL单表的数据量是500w-1000w之间性能比较好,超过1000w性能也会下降。

③ 硬件问题 因为单个服务的磁盘空间是有限制的,如果并发压力下所有的请求都访问同一个节点,肯定会对磁盘IO造成非常大的影响。

3 数据库性能优化
① 参数优化 ② 缓存、索引 ③ 读写分离 ④ 分库分表 (最终方案)

4 分库分表介绍
4.1 使用背景
表的数量达到了几百上千张表时,众多的业务模块都访问这个数据库,压力会比较大,考虑对其进行分库。
表的数据达到了几千万级别,在做很多操作都比较吃力,考虑对其进行分库或者分表
4.2 数据切分方案
数据的切分(Sharding)根据其切分规则的类型,可以分为两种垂直切分和水平切分模式
4.2.1垂直切分
按照业务模块进行切分,将不同模块的表切分到不同的数据库中。
4.2.1.1 分库

4.2.1.2 分表
按照字段将大表拆分成小表,另当表中含有Blob、Clob(用于存头像、小图片等)等二进制类型的字段时,因其不能使用索引,考虑性能问题需将其拆分出来。

4.2.2 水平切分
将一张大表按照一定的切分规则,按照行切分成不同的表或者切分到不同的库中
4.2.2.1 范围式拆分
好处:增删数据库实例时数据迁移是部分迁移,扩展能力强。
坏处:热点数据分布不均,访问压力不能负载均衡。

4.2.2.2 hash式拆分
好处:热点数据分布均匀,访问压力能负载均衡。
坏处:增删数据库实例时数据都要迁移,扩展能力差。
4.2.2.3 水平切分规则
① 按照ID取模:对ID进行取模,余数决定该行数据切分到哪个表或者库中。
② 按照日期:按照年月日,将数据切分到不同的表或者库中。
③ 按照范围:可以对某一列按照范围进行切分,不同的范围切分到不同的表或者数据库中。
4.2.3 切分原则
① 能不切分尽量不要切分。
② 如果要切分一定要选择合适的切分规则,提前规划好。
③ 数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能。
4.2.4 说明
垂直切分是程序员切分,水平切分是利用TDDL、Cobar、Mycat 、sharding-jdbc等进行切分。

4.3 分库分表需要解决的问题
4.3.1 分布式事务问题
解决方案: ① 采用补偿事务,例如TCC来解决分布式事务问题。 ② 用记录日志等方式来解决分布式事务问题。

4.3.2 分布式主键ID冲突问题
解决方案: ① 利用Redis的incr命令生成主键。 ② 用UUID生成主键(不建议:字段比较长、不好排序)。 ③ 利用snowake算法生成主键。

4.3.3 跨库join问题

解决方案: ① 将有E-R关系的表存储到一个库中。 ② 对于数据量少的表建成全局表,分布到各个库中 ③ 对于必须跨库join的,最多支持跨两张表的跨库join

4.4 案例分析
情况: 有用户表user(uid、name、city、sex、age、timestamp),共5亿条数据,机器为x86 64位系统,查询维度比较单一

问题: 分几张表?PartitionKey如何选择?

分析: 根据分表原则,单行数据大于100字节则1千万一张表,单行数据小于100字节则5千万一张表,用户表单行数据小于100字节,单张表可存5千条记录,5亿除以5千万等于10,向上取整,共分为16张表。city、timestamp做为PartitionKey会造成热点数据分布不均匀,故使用uid作为PartitionKey,算法为uid模以16

4.5 分库分表实现技术
① 阿里的TDDL、Cobar
② 基于阿里Cobar开发的
③ 当当网的sharding-jdbc
5 Sharding JDBC
5.1 Sharding JDBC 简介
Apache Sharding Sphere(Incubator) 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立,却又能够混合部署配合使用的产品组成。

5.1.1 Sharding JDBC 架构

5.1.2 Sharding JDBC 对多数据库的支持

5.1.3 Sharding JDBC 核心概念
数据分片:将数据按照一定的规则进行切分得到数据分片,数据分片分为垂直分片和水平分片。

分片键:用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。

一致性hash环:

逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。 真实表:在分片的数据库中真实存在的物理表。 数据节点:数据分片的最小单元。由数据源名称和数据表组成。 绑定表:指分片规则一致的主表和子表。例如: t_order 表和t_order_item 表,均按照order_id 分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。 广播表:指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不 大且需要与海量数据的表进行关联查询的场景,例如:字典表。

5.2 分片算法与分片策略
5.2.1 分片算法
Sharding-JDBC的分片算法有精确分片算法、范围分片算法、复合分片算法、Hint分片算法四种。

5.2.1.1 精确分片算法
用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。

public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
for (String each : collection) {
if(each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+“”)){
return each;
}
}
throw new IllegalArgumentException();
}
}
5.2.1.2 范围分片算法
用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。

public class MyRangeShardingAlgorithm implements RangeShardingAlgorithm {
@Override
public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) {
log.info(“Range collection:”

  • JSON.toJSONString(collection)
  • “,rangeShardingValue:”
  • JSON.toJSONString(rangeShardingValue));
    Collection collect = new ArrayList<>();
    Range valueRange = rangeShardingValue.getValueRange();
    for (Long i = valueRange.lowerEndpoint(); i <=valueRange.upperEndpoint(); i++) {
    for (String each : collection) {
    if (each.endsWith(i % collection.size() + )) {
    collect.add(each);
    }
    }
    }
    return collect;
    }
    }
    5.2.1.3 复合分片算法
    用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。

public class MyComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm{

@Override
public Collection doSharding(Collection collection, Collection shardingValues) {
log.info(“collection:”

  • JSON.toJSONString(collection)
  • “,shardingValues:”
  • JSON.toJSONString(shardingValues));

Collection orderIdValues = getShardingValue(shardingValues, “order_id”);
Collection userIdValues = getShardingValue(shardingValues, “user_id”);
List shardingSuffix = new ArrayList<>();
/*例如:根据user_id + order_id 双分片键来进行分表/
//Set<List> valueResult = Sets.cartesianProduct(userIdValues, orderIdValues);
for (Long userIdVal : userIdValues) {
for (Long orderIdVal : orderIdValues) {
String suffix = userIdVal % 2 + “_” + orderIdVal % 2;
collection.forEach(x -> {if (x.endsWith(suffix)) { shardingSuffix.add(x);}});
}
}
return shardingSuffix;
}

private Collection getShardingValue(Collection shardingValues, final String key) {
Collection valueSet = new ArrayList<>();
Iterator iterator = shardingValues.iterator();
while (iterator.hasNext()) {
ShardingValue next = iterator.next();
if (next instanceof ListShardingValue) {
ListShardingValue value = (ListShardingValue) next;

if (value.getColumnName().equals(key)) {
return value.getValues();
}
}
}
return valueSet;
}

}
5.2.1.4 Hint分片算法
用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。

5.2.2 分片策略
Sharding-JDBC的分片策略有标准分片策略、复合分片策略、行表达式分片策略、Hint分片策略四种。

5.2.2.1 标准分片策略
提供对SQL语句中的=、IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片。如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。

5.2.2.2 复合分片策略
提供对SQL语句中的=、IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
5.2.2.3 行表达式分片策略
使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发。 行表达式的使用非常直观,只需要在配置中使用->{ expression } TODO标识行表达式即可。 目前支持数据节点和分片算法这两个部分的配置。行表达式的内容使用的是Groovy的语法, Groovy能够支持的所有操作,行表达式均能够支持。

5.2.2.4 Hint分片策略
通过Hint而非SQL解析的方式分片的策略。对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。
5.3 Sharding JDBC核心组件
5.3.1 解析引擎

SQL语句经过解析引擎的词法解析、语法解析,形成语法树。

5.3.2 路由引擎

标准路由是ShardingSphere最为推荐使用的分片方式,它的适用范围是不包含关联查询或仅包含绑定 表之间关联查询的SQL。当分片运算符是等于号时,路由结果将落入单库(表),当分片运算符是BETWEEN或IN时,则路由结果不一定落入唯一的库(表),因此一条逻辑SQL最终可能被拆分为多条用于执行的真实SQL。

5.3.3 改写引擎

工程师面向逻辑库与逻辑表书写的SQL,并不能够直接在真实的数据库中执行,SQL改写用于将逻辑SQL改写为在真实数据库中可以正确执行的SQL。 它包括正确性改写和优化改写两部分。

5.3.4 执行引擎

ShardingSphere采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。执行引擎的目标是自动化的平衡资源控制与执行效率。执行引擎分为准备和执行两个阶段

5.3.5 归并引擎

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。 ShardingSphere支持的结果归并从功能上分为遍历、排序、分组、分页和聚合5种类型,它们是组合而 非互斥的关系。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值