关于Sharding-Sphere分库分表框架的保姆级教学!

news/2024/12/29 8:33:51/

在分库分表领域中,其实有许许多多的一些落地技术栈,如TDDL、TSharding、Sharding-Sphere、MyCat、Atlas、Oceanus、Vitess.....,但经时间沉淀与岁月洗礼后,如今主流的方案也就剩下了MyCat、Sharding-Sphere两种,MyCat近几年由于某些原因,开始逐渐走下坡路,反观投入Apache怀抱的Sharding-Sphere热度逐步上升,其目前的最新版本也相对稳定。

正是由于上述原因,如今越来越多的企业选用Sharding-Sphere作为分库分表的落地方案

一、初识Apache-Sharding-Sphere生态


Apache-Sharding-Sphere的前身是当当网开源的Sharding-JDBC框架,后面引入Zookeeper作为注册中心,又研发了Sharding-Proxy中间件,贡献给Apache软件基金会后,正式整合成Sharding-Sphere生态,并且支持、兼容各种数据库,Apache-Sharding-Sphere官网上可看到的发展历程如下:

目前最新的5.2.1版本属于其中5.x阶段的一个版本,目前支持可可拔插功能,其支持的核心功能如下:

在该版本中都有对应的解决方案,如多表连查、数据分片、分布式事务、数据迁移与扩容、分布式ID.....,在预计2023年发布的6.x版本中会结合各类云容器技术,全面兼容与拥抱云生态,在预计2025年发布的7.x版本中,则会彻底落实Databases-Plus理念,支持将各类数据库作为可拔插式存储引擎使用,也意味着像MySQL的可拔插式引擎那样,在Sharding-Sphere中使用MySQL、Oracle、PgSQL、SQL-Server....等关系型数据。

其实看到这里大家会发现,Sharding-Sphere作为Apache软件基金会的顶级项目,其内部对于它的未来规划十分明确,主要就是构建一个分库分表技术的生态圈,就类似于Spring框架在J2EE中的地位一样,迄今为止已发行的5.x系列版本,已经具备应用于生产环境的能力,其中对于分库分表核心的主干功能都已实现,从官网的发展历程来看,后续版本都属于修修补补的性质,主要是为了让其生态更完善。

Apache-Sharding-Sphere总共由JDBC、Proxy、Sidecar三大核心产品组成,前两者都已具备完整形态,Sidecar还处于开发阶段,这也就代表着目前的ShardingSphere5中只有JDBC、Proxy两款产品,这两款产品即支持各自独立部署,也支持混合部署的模式,两者区别在于:

  • JDBC:以工程形式嵌入Java应用,兼容所有JDBC支持的数据库,适用于任意ORM框架。
  • Proxy:以独立的中间件形式部署,目前只支持MySQL、PgSQL,但支持异构语言开发的系统。

1.1、Sharding-JDBC框架简介

Sharding-JDBC的定位是一款轻量级Java框架,它会以POM依赖的形式嵌入程序,运行期间会和Java应用共享资源,这款框架的本质可以理解成是JDBC的增强版,只不过Java原生的JDBC仅支持单数据源的连接,而Sharding-JDBC则支持多数据源的管理,部署形态如下:


Java-ORM框架在执行SQL语句时,Sharding-JDBC会以切面的形式拦截发往数据库的语句,接着根据配置好的数据源、分片规则和路由键,为SQL选择一个目标数据源,然后再发往对应的数据库节点处理。

Sharding-JDBC在整个业务系统中对性能损耗极低,但为何后面又会推出Sharding-Proxy呢?因为Sharding-JDBC配置较为麻烦,比如在分布式系统中,任何使用分库分表的服务都需要单独配置多数据源地址、路由键、分片策略....等信息,同时它也仅支持Java语言,当一个系统是用多语言异构的,此时其他语言开发的子服务,则无法使用分库分表策略。

1.2、Sharding-Proxy中间件简介

也正是由于配置无法统一管理、不支持异构系统的原因,后面又引入Sharding-Proxy来解决这两个问题,Sharding-Proxy可以将其理解成一个伪数据库,对于应用程序而言是完全透明的,它会以中间件的形式独立部署在系统中,部署形态如下:


使用Sharding-Proxy的子服务都会以连接数据库的形式,与其先建立数据库连接,然后将SQL发给它执行,Sharding-Proxy会根据分片规则和路由键,将SQL语句发给具体的数据库节点处理,数据库节点处理完成后,又会将结果集返回给Sharding-Proxy,最终再由它将结果集返回给具体的子服务。

Sharding-Proxy虽然可以实现分库分表配置的统一管理,以及支持异构的系统,但因为需要使用独立的机器部署,同时还会依赖Zookeeper作为注册中心,所以硬件成本会直线增高,至少需要多出3~4台服务器来部署。

同时SQL执行时,需要先发给Proxy,再由Proxy发给数据库节点,执行完成后又会从数据库返回到Proxy,再由Proxy返回给具体的应用,这个过程会经过四次网络传输的动作,因此相较于原本的Sharding-JDBC来说,性能、资源开销更大,响应速度也会变慢。

1.3、JDBC、Proxy混合部署模式

如果用驱动式分库分表,虽然能够让Java程序的性能最好,但无法支持多语言异构的系统,但如果纯用代理式分库分表,这显然会损害Java程序的性能,因此在Sharding-Sphere中也支持JDBC、Proxy做混合式部署,也就是Java程序用JDBC做分库分表,其他语言的子服务用Proxy做分库分表,部署形态如下:


这种混合式的部署方案,所有的数据分片策略都会放到Zookeeper中统一管理,然后所有的子服务都去Zookeeper中拉取配置文件,这样就能很方便的根据业务情况,来灵活的搭建适用于各种场景的应用系统,这样也能够让数据源、分片策略、路由键....等配置信息灵活,可以在线上动态修改配置信息,修改后能够在线上环境中动态感知。

Sharding-Sphere还提供了一种单机模式,即直接将数据分片配置放在Proxy中,但这种方式仅适用于开发环境,因为无法将分片配置同步给多个实例使用,也就意味着会导致其他实例由于感知不到配置变化,从而造成配置信息不一致的错误。

二、Sharding-Sphere中的核心概念

   分库分表中最重要的核心概念有两个,即路由键和分片算法,这两个将决定数据分片的位置,先稍微解释一下这两个概念:

  • 路由键:也被称为分片键,也就是作为数据分片的基准字段,可以是一个或多个字段组成。
  • 分片算法:基于路由键做一定逻辑处理,从而计算出一个最终节点位置的算法。

举个例子来感受一下,好比按user_id将用户表数据分片,每八百万条数据划分一张表,那在这里,user_id就是路由键,而按user_id做范围判断则属于分片算法,一张表中的所有数据都会依据这两个基础,后续对所有的读写SQL进行改写,从而定位到具体的库、表位置。

2.1、分库分表的工作流程


Sharding-Sphere这套技术中,无论是JDBC还是Proxy产品,工作的流程都遵循上述这个原则,里面除开上面介绍的路由键和分片算法的概念外,还有逻辑表、真实表、数据节点这三个概念:

  • 逻辑表:提供给应用程序操作的表名,程序可以像操作原本的单表一样,灵活的操作逻辑表。
  • 真实表:在各个数据库节点上真实存在的物理表,但表名一般都会和逻辑表存在偏差。
  • 数据节点:主要是用于定位具体真实表的库表名称,如DB1.tb_user1、DB2.tb_user2.....
    • 均匀分布:指一张表的数量在每个数据源中都是一致的。
    • 自定义分布:指一张表在每个数据源中,具体的数量由自己来定义,上图就是一种自定义分布。

Java程序为例,编写业务代码时写的SQL语句,会直接基于逻辑表进行操作,逻辑表并不是一种真实存在的表结构,而是提供给Sharding-Sphere使用的,当Sharding-Sphere接收到一条操作某张逻辑表的SQL语句时,它会根据已配置好的路由键和分片算法,对相应的SQL语句进行解析,然后计算出SQL要落入的数据节点,最后再将语句发给具体的真实表上处理即可。

Sharding-Sphere-JDBC、Proxy的主要区别就在于:解析SQL语句计算数据节点的时机不同,JDBC是在Java程序中就完成了相应的计算,从Java程序中发出的SQL语句就已经是操作真实表的SQL了。而Proxy则是在Java应用之外做解析工作,它会接收程序操作逻辑表的SQL语句。然后再做解析得到具体要操作的真实表,然后再执行,同时Proxy还要作为应用程序和数据库之间,传输数据的中间人。

2.2、Sharding-Sphere中的表概念

除开上述的一些核心概念外,在Sharding-Sphere中为了解决某些问题,同时还有一些表概念,如广播表、绑定表、单表、动态表等,接着简单介绍一下这些概念。

2.2.1、绑定表


当多张表之间存在物理或逻辑上的主外键关系,如果无法保障同一主键值的外键数据落入同一节点,显然在查询时就会发生跨库查询,这无疑对性能影响是极大的,所以在其中也提到过可以使用绑定表的形式解决该问题。

比如前面案例中的order_id、order_info_id可以配置一组绑定表关系,这样就能够让订单详情数据随着订单数据一同落库,简单的说就是:配置绑定表的关系后,外键的表数据会随着主键的表数据落入同一个库中,这样在做主外键关联查询时,就能有效避免跨库查询的情景出现。

2.2.2、广播表


当有些表需要经常被用来做连表查询时,这种频繁关联查询的表,如果每次都走跨库Join,这显然又会造成一个令人头疼的性能问题,所以对于一些经常用来做关联查询的表,就可以将其配置为广播表,广播表在有些地方也被称为同步表、网络表、全局表,但本质上表达的含义都相同,如下:

广播表是一种会在所有库中都创建的表,以系统字典表为例,将其配置为广播表之后,向其增、删、改一条或多条数据时,所有的写操作都会发给全部库执行,从而确保每个库中的表数据都一致,后续在需要做连表查询时,只需要关联自身库中的字典表即可,从而避免了跨库Join的问题出现。

2.2.3、单表

单表的含义比较简单,并非所有的表都需要做分库分表操作,所以当一张表的数据无需分片到多个数据源中时,就可将其配置为单表,这样所有的读写操作最终都会落入这一张单表中处理。

2.2.4、动态表

动态表的概念在Sharding-Sphere最新的5.x文档中已经移除了,但也可以基于分片算法去实现,所以虽然移除了动态表的概念,但也可以实现相同的效果,动态表的概念是指表会随着数据增长、或随着时间推移,不断的去创建新表,如下:


之前为了处理单月数据增长过高的问题,咱们需要手动实现了一套按月动态分表的方案,但在Sharding-Sphere中可以直接支持配置,无需自己去从头搭建,因此实现起来尤为简单,配置好之后会按照时间或数据量动态创建表。

2.3、Sharding-Sphere中的数据分片策略

前面聊到过,分库分表之后读写操作具体会落入哪个库中,这是根据路由键和分片算法来决定的,而Sharding-Sphere中的数据分片策略又分为:内置的自动化分片算法、用户自定义的分片算法两大类,Sharding-Sphere内置的算法涵盖取模分片、哈希分片、范围分片、时间分片等这积累常规算法,而自定义分片算法又可细分为:

  • 标准分片算法:适合基于单一路由键进行=、in、between、>、<、>=、<=...进行查询的场景。
  • 复合分片算法:适用于多个字段组成路由键的场景,但路由算法需要自己继承接口重写实现。
  • 强制分片算法:适用于一些特殊SQL的强制执行,在这种模式中可以强制指定处理语句的节点。

综上所述,在Sharding-Sphere内部将这四种分片策略称为:Inline、Standard、Complex、Hint,分别与上述四种策略一一对应,但这四种仅代表四种策略,具体的数据分片算法,可以由使用者自身来定义(后续会结合代码实战讲解)。

2.4、Sharding-Sphere的分库方式

Sharding-Sphere生态中,支持传统的主从集群分库,如搭建出读写分离架构、双主双写架构,同时也支持按业务进行垂直分库,也支持对单个库进行横向拓展,做到水平分库。

但通常都是用它来实现水平分库和读写分离,因为分布式架构的系统默认都有独享库的概念,也就是分布式系统默认就会做垂直分库,因此无需引入Sharding-Sphere来做垂直分库。

因此接下来会搭建一个简单的SpringBoot+MyBatis项目,结合Sharding-Sphere-JDBC实现水平分库~

三、SpringBoot整合Sharding-JDBC框架

   SpringBoot作为一个脚手架框架,用于整合第三方框架十分轻松,比如目前想要引入Sharding-Sphere-JDBC来做分库分表,只需要在pom.xml中加入如下依赖即可:

<!-- shardingsphere-jdbc-jdbc依赖 -->
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.2.1</version>
</dependency>
复制代码

目前Sharding-Sphere最新的版本就是2022.08月发布的5.2.1,因此这里先引入最新的依赖作为学习版本,如若是线上业务则可落后最新的一到两个版本,或者选择官方推荐的稳定版本。

3.1、搭建项目的基础结构

   接着先在数据库中创建db_sharding_01、db_sharding_02两个库,我这里用伪集群的方式搭建水平库,毕竟线上只需要把数据库地址改为不同的机器IP即可,SQL如下:

-- 先将编码格式改为utf8mb4
set names utf8mb4;
set foreign_key_checks = 0;-- 接着创建两个数据库
create databases db_sharding_01;
create databases db_sharding_02;
复制代码

接着分别再在两个水平库中,创建用户表、订单表、订单详情表、商品表(两张),这四张表是接下来用于测试的表,SQL如下:

-- >>>>>>>>>>创建用户表<<<<<<<<<<<
drop table if exists `user_info`;
create table `user_info`  (`user_id` bigint not null comment '用戶id',`user_name` varchar(255) comment '用戶姓名',`user_sex` varchar(255) comment '用戶性別',`user_age` int(8) not null comment '用戶年齡',primary key (`user_id`) using btree
)
engine = InnoDB
character set = utf8
collate = utf8_general_ci 
row_format = compact;-- >>>>>>>>>>创建商品表1<<<<<<<<<<<
drop table if exists `shoping_00`;
create table `shoping_00`  (`shoping_id` bigint not null comment '商品id',`shoping_name` varchar(255) comment '商品名称',`shoping_price` int(8) not null comment '商品价格',primary key (`shoping_id`) using btree
)
engine = InnoDB
character set = utf8
collate = utf8_general_ci 
row_format = compact;-- >>>>>>>>>>创建商品表2<<<<<<<<<<<
drop table if exists `shoping_01`;
create table `shoping_01`  (`shoping_id` bigint not null comment '商品id',`shoping_name` varchar(255) comment '商品名称',`shoping_price` int(8) not null comment '商品价格',primary key (`shoping_id`) using btree
)
engine = InnoDB
character set = utf8
collate = utf8_general_ci 
row_format = compact;-- >>>>>>>>>>创建订单表<<<<<<<<<<<
drop table if exists `order`;
create table `order`  (`order_id` bigint not null comment  '订单号',`order_price` int(8) not null comment '订单总金额',`user_id` bigint not null comment '用戶id',primary key (`order_id`) using btree
) 
engine = InnoDB
character set = utf8
collate = utf8_general_ci 
row_format = compact;-- >>>>>>>>>>创建订单详情表<<<<<<<<<<<
drop table if exists `order_info`;
create table `order_info`  (`order_info_id` bigint not null comment  '订单详情号',`order_id`  bigint not null comment '订单号',`shoping_name` varchar(255)  comment '商品名称',`shoping_price` int(8) not null comment '商品价格',primary key (`order_info_id`) using btree,index `key_order_id`(`order_id`) using btree
)
engine = InnoDB
character set = utf8
collate = utf8_general_ci 
row_format = compact;
复制代码

库结构和表结构创建完成后,接着是Java端的dao、service层的逻辑代码,但对于mapper、xml文件、service的逻辑代码就在此先忽略,这些大家都会的基操步骤,就不写出来占用篇幅啦,后续会附上源码地址的,最终搭建出的项目结构如下:


虽然看起来代码不少,但俺也是通过工具快速生成的,基本上敲几下键盘~,到这里为止,项目的基础结构就搭建完成啦,后面开始真正的分库分表配置。

3.2、分库分表的核心配置

   之前提到过,Sharding-Sphere的所有产品对业务代码都是零侵入的,无论是Sharding-JDBC也好,Sharding-Proxy也罢,都不需要更改业务代码,这也就意味着大家在分库分表环境下做业务开发时,可以像传统的单库开发一样轻松,Sharding-Sphere中最主要的是对配置文件的更改,Sharding-JDBC主要修改application.properties/yml文件,Sharding-Proxy主要修改自身的配置文件。

但这里要注意:SpringBoot整合Sharding-JDBC时,官方更加推荐使用properties的方式做分库分表配置,这样能够让Sharding-Sphere更好的解析,如果使用yml配置时会出现解析问题,这里需要手动做调整,也就是引入snakeyaml的解析包,否则可能导致解析出现错误。

3.2.1、多数据源配置

接着来聊聊Sharding-JDBC的配置方式,如下:

spring:shardingsphere:# 将运行模式配置为Standalone单机模式(Cluster:集群模式)mode:type: Standalonerepository:type: JDBC# 配置多个数据源datasource:names: ds0,ds1# 配置第一个数据源ds0:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: 「数据库节点1的地址」username: 「数据库节点1的账号」password: 「数据库节点1的密码」# 配置第二个数据源ds1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: 「数据库节点2的地址」username: 「数据库节点1的账号」password: 「数据库节点1的密码」
复制代码

上述这组配置中,需要通过names配置多个数据源的别名,接着需要为每个别名配置对应的数据源信息,按照上述方式编写好配置文件后,则表示完成了多数据源的配置。

3.2.2、多数据源可用性测试

为了确保多数据源的可用性,接着先简单配置一张表:

spring:shardingsphere:# 执行时显示SQL语句props:# 日志显示具体的SQLsql-show: true# 配置分片规则rules:# 配置分片策略sharding:# 配置所有分片表tables:# 首先配置商品表的分片策略shoping:# 声明商品表所在的真实数据节点(这里先显式声明一个节点测试)actual-data-nodes: ds0.shoping_00
复制代码

然后撰写一个测试用例,来测试一下多数据源的配置是否有效:

@SpringBootTest
public class DbShardingJdbcApplicationTests {@Testvoid contextLoads() {}
}// shoping商品表的测试类
class ShopingServiceImplTest extends DbShardingJdbcApplicationTests {@Autowiredprivate ShopingService shopingService;// 测试数据插入的方法@Testvoid insertSelective() {Shoping shoping = new Shoping();shoping.setShopingId(11111111L);shoping.setShopingName("黄金零号竹子");shoping.setShopingPrice(8888);shopingService.insertSelective(shoping);}
}
复制代码

执行上述测试用例后,会在控制台看到如下日志:

2022-11-25 14:41:23.096  INFO 17748 --- [main] ShardingSphere-SQL: Logic SQL: insert into shoping( shoping_id, shoping_name, shoping_price ) values ( ?, ?,? )2022-11-25 14:41:23.096 INFO 17748 --- [main] ShardingSphere-SQL: SQLStatement:MySQLInsertStatement(.....)2022-11-25 14:41:23.096  INFO 17748 --- [main] ShardingSphere-SQL: Actual SQL: ds0 ::: insert into shoping_00( shoping_id,shoping_name,shoping_price ) values (?, ?, ?) ::: [11111111, 黄金零号竹子, 8888]
复制代码

前面的Logic-SQL逻辑语句操作的是shoping表,但后面Actual-SQL真实语句是在操作ds0.shoping_00表,最终查询一下数据库的表是否有数据,如下:

select * from db_sharding_01.shoping_00;
+------------+--------------------+---------------+
| shoping_id | shoping_name       | shoping_price |
+------------+--------------------+---------------+
|   11111111 | 黄金零号竹子       |          8888 |
+------------+--------------------+---------------+
复制代码

此时会发现表中出现了前面插入的测试数据,这也就意味着多数据源的配置已生效。

3.2.3、inline行表达式

接着可以再配置多个真实数据节点:

actual-data-nodes: ds0.shoping_00,ds0.shoping_01,ds1.shoping_00,ds1.shoping_01
复制代码

可以通过上述这种方式,以逗号隔开多个真实数据节点,但这种方式在分片节点较多的情况下,配置起来就较为麻烦,因此也可直接用Sharding-Sphere支持的行表达式语法来快捷编写,如下:

actual-data-nodes: ds$->{0..1}.shoping_0$->{0..1}# 释义:
ds$->{0..1}则表示:ds0、ds1
# 也可以这样写:
ds$->{['0','1']}
# 也可以组合起来用:
ds$->{['0','1']}.shoping_0$->{0..1}
复制代码

上述两者之间的区别主要在于:前者只能配置连续、均匀的分片节点,而后者相对灵活很多,可以自行指定分片节点,两种表达式语法也可结合使用,这样能够在分片不均匀的特殊场景下,灵活适用于各类业务。

3.2.4、配置分库策略

接着需要配置分库策略,也就是指定路由键和分片算法,如下:

spring:shardingsphere:# 配置分片规则rules:# 配置分片策略sharding:# 配置所有分片表tables:# 首先配置商品表的分片策略shoping:# 声明商品表所在的真实数据节点(这里先写死表名,便于测试)actual-data-nodes: ds$->{0..1}.shoping_00# 配置分库规则database-strategy:standard:# 配置路由键为shoping_id(数据库中的列名)sharding-column: shoping_id# 配置分片算法(需要配置一个名词,通过别名指向具体的策略)sharding-algorithm-name: db-inline-modsharding-algorithms:# 配置前面的分库算法db-inline-mod:# 声明是 INLINE 简单类型的分片type: INLINEprops:# 选择对shoping_id做取模运算algorithm-expression: ds$->{shoping_id % 2}
复制代码

接着依旧撰写一个简单的测试用例,来实验一下分库策略是否有效,如下:

/*** 测试分库策略是否有效* **/
@Test
void databaseStrategyInsert() {for (int i = 1; i <= 10; i++){Shoping shoping = new Shoping();shoping.setShopingId((long) i);shoping.setShopingName("黄金"+ i +"号竹子");shoping.setShopingPrice(1111 * i);shopingService.insertSelective(shoping);}
}
复制代码

按照咱们配置的对shoping_id做取模分库,理论上数据应该呈现下述形式:

  • ds0(db_sharding_01)2、4、6、8、10
  • ds1(db_sharding_02)1、3、5、7、9

那么来运行测试案例,查询一下两个库的shoping_00表的数据看看,如下:


很显然,结果与咱们想象的数据一致,但上述配置中的取模算法,也可以直接使用Sharding-Sphere内置的取模算法,配置方式如下:

sharding-algorithms:# 配置一个取模算法key-int-mod:# 使用ShardingSphere内置的取模算法type: MODprops:# 声明分库的节点数量sharding-count: 2
复制代码

通过使用内置分片算法的形式去做取模也是可以的,官方内置了取模、哈希取模、时间范围、数据范围、容量范围等多种简单的分片算法。

3.2.5、配置分表策略

上面对分库规则做了配置后,那接着来配置一下分表策略,分表的路由键可以与分库的路由键不同,也可以相同,这点可以根据业务来决定,比如我这里就使用商品名称作为分表路由键,如下:

spring:shardingsphere:# 配置分片规则rules:# 配置分片策略sharding:# 配置所有分片表tables:# 首先配置商品表的分片策略shoping:# 声明商品表所在的真实数据节点(把原本写死的表名改成表达式)actual-data-nodes: ds$->{0..1}.shoping_0$->{0..1}# 配置分表规则table-strategy:standard:# 配置分表的路由键:商品名称sharding-column: shoping_namesharding-algorithm-name: key-hash-modsharding-algorithms:# 配置哈希取模的分表算法key-hash-mod:# 使用内置的哈希取模算法type: HASH_MODprops:# 声明分表的节点数量sharding-count: 2
复制代码

在原本分库配置的基础上,再次新增上述分表配置,但因为选择了shoping_name作为分表路由键,因此无法使用简单的取模分片算法,这里就选用了哈希取模分片算法,先对商品名称做一次哈希处理,接着再使用哈希值做取模运算,接着来撰写测试用例:

/*** 测试按商品名称的分表策略是否有效* **/
@Test
void tableStrategyInsert() {for (int i = 1; i <= 20; i++){Shoping shoping = new Shoping((long) i, "白玉"+ i +"号竹子", i * 888);shopingService.insertSelective(shoping);}
}
复制代码

测试之前先将原本表中的数据清空,接着执行上述代码,数据库中的结果如下:


此时观察四张表中的数据,数据并未出现重复,但插入的20条测试数据,是怎么到每张表中去的呢?首先会根据shoping_id做取模运算,将偶数ID全部落入ds0(sharding_01)库,将奇数ID全部落入ds1(sharding_02)库,接着再基于shoping_name做哈希取模,将数据再分发到具体的表中。

当然,上述我仅只是用于参考学习,在线上环境时,对于路由键的选择一定要慎重,这将关乎到所有读写请求的走向,在路由键、分片算法配置不合理的情况下,可能会导致读写操作变得尤为复杂。

3.2.6、数据查询测试

上面配置好分库分表的规则后,插入数据都没有问题,接着再来试试查询场景,下面撰写两个测试用例,分别查询单条数据,以及查询所有数据,如下:

/*** 根据商品ID查询单条数据* **/
@Test
void findByShopingID() {Shoping shoping = shopingService.selectByPrimaryKey(1L);System.out.println(shoping);
}
复制代码

此时运行该测试用例会出现如下日志:

2022-11-25 16:38:22.333  INFO 15708 --- [main] ShardingSphere-SQL: Logic SQL: select shoping_id, shoping_name, shoping_pricefrom shopingwhere shoping_id = ?2022-11-25 16:38:22.334  INFO 15708 --- [main] ShardingSphere-SQL: Actual SQL: ds1 :::select shoping_id, shoping_name, shoping_pricefrom shoping_00where shoping_id = ? UNION ALL select shoping_id, shoping_name, shoping_pricefrom shoping_01where shoping_id = ?
::: [1, 1]Shoping{shopingId=1, shopingName='白玉1号竹子', shopingPrice=888}
复制代码

此时数据的确查询出来了,但注意上述最终执行的语句,此时会发现会通过UNION ALL拼接查询两张表,这是为什么呢?因为咱们之前分表选择的路由键为shoping_name,但此时是通过shoping_id在查询数据,ShardingSphere只能根据ID确定查询的库,但无法确定当前查询的数据位于哪张表,所以只能将两张表全部查询一次,最终才能得到shopingId=1的商品数据。

从这里相信大家就能明显感受出:选择一个合适的字段作为路由键的重要性,如果路由键设计的不合理,这会导致出现大量不必要产生的查询开销,因此大家在实际业务中,对路由键的选择一定要慎重!慎重!再慎重!!!

接着再撰写一个查询所有数据的测试用例,如下:

/*** 查询所有商品数据* **/
@Test
void queryAllShopingData() {List<Shoping> shopings = shopingService.getAll();shopings.forEach(System.out::println);
}
复制代码

执行结果如下:


此时大家会发现,虽然将前面插入的所有数据都查询出来了,但显然没有了顺序,这是因为Sharding-Sphere会直接按照查询表的顺序组装结果集,因此数据是无序的,如果要求按shoping_id做排序,那可以将SQL语句最后加上order by shoping_id asc,这样Sharding-Sphere在组装数据时,会自动按shoping_id从大到小做排序。

不过虽然Sharding-Sphere支持order by这种语句,但对于很多语法并不支持,如批量插入语句、批量修改语句、复杂的聚合函数、子查询、having查询、跨库Join查询、CASE WHEN查询等复杂性较高的语句。

3.2.7、分布式序列生成算法

前面叨叨絮絮了很多内容,但这里遗忘了一个较为致命的问题,因为前面所有的插入测试都是咱们手动指定了主键ID值,但实际业务中更多会依赖于数据库的自增机制,以此来确保主键的唯一性和递增性,但在之前讲到过:

如果在分库环境中再依赖于数据库自身的自增机制,这显然会造成ID重复的问题出现,虽然能够通过设置自增步长的方式解决,但这种形式对后续的扩容又不大友好,因此在分布式场景中,急需一种既能确保全局唯一、又能保障顺序递增的技术出现,以此来解决ID重复这个棘手问题。

在早期的分布式系统中遇到该问题时,为了确保主键的唯一性,只能放弃递增性,选择无序的UUID来作为主键值,直到TwitterSnowflake雪花算法开源后,基本上雪花算法成为了分布式ID的主流方案,而对于该算法,MyBatis-Plus、Sharding-Sphere中都有内置的支持,咱们首先来做个简单配置:

spring:shardingsphere:# 配置分片规则rules:# 配置分片策略sharding:# 配置所有分片表tables:# 首先配置商品表的分片策略shoping:# 配置shoping表的主键生成策略key-generate-strategy:# 声明主键为shoping_idcolumn: shoping_id# 同样指向global-id-snowflake这个具体的主键生成策略keygenerator-name: global-id-snowflakekey-generators:# 配置上面的主键生成策略global-id-snowflake:# 选择使用内置的雪花算法type: SNOWFLAKEprops:# 分配一个工作节点ID(要确保全局唯一)worker-id: 111
复制代码

上述这组配置的含义是:shoping_id的值将通过雪花算法来生成,接着编写一个测试用例,来简单试试效果,如下:

/*** 测试分布式序列算法 - 雪花算法的效果* **/
@Test
void insertSnowflake() {for (int i = 1; i <= 10; i++) {Shoping shoping = new Shoping();shoping.setShopingName("黄金"+ i +"号竹子");shoping.setShopingPrice(8888);shopingService.insertSelective(shoping);}
}
复制代码

注意看,在这个测试用例中咱们就没有手动指定shoping_id值了,接着先将原本库中的表数据清空,然后运行后表的数据如下:


图中圈出的一连串数字,即是Sharding-Sphere为咱们生成的分布式ID,那究竟雪花算法是如何保障全局唯一性的呢?接下来一起深入聊聊雪花算法的实现原理。

3.2.8、Snowflake雪花算法的实现原理

雪花算法生成的分布式ID,在Java中会使用Long类型来承载,Long类型占位8bytes,也就正好对应上述这张图的64个比特位,这64bit会被分为四部分:

  • 符号位(1bit):永远为零,表示生成的分布式ID为正数。
  • 时间戳位(2~42bit):会将当前系统的时间戳插入到这段位置。
  • 工作进程位(43~53bit):在集群环境下,每个进程唯一的工作ID
  • 序列号位(54~64bit):该序列是用来在同一个毫秒内生成不同的序列号。

当需要生成一个分布式ID时,Sharding-Sphere首先会获取当前系统毫秒级的时间戳,放入到第2~42bit,总共占位41个比特,一年365天中,一共会存在365*24*60*60*1000个不同的毫秒时间戳,此时可以做组计算:

Math.pow(2, 41) / (365*24*60*60*1000) ≈ 69.73年
复制代码

也就是41bit的空间,可以存下大概69.73年生成的毫秒时间戳,Sharding-Sphere雪花算法的时间纪元是从2016.11.01日开始的,这也就代表着使用Sharding-Sphere雪花算法生成的分布式ID,在未来近70年内无需担心出现时间戳存不下的问题。

有人也许会纠结,万一我的系统会在线上运行百年之久呢?这种情况下,获取到的时间戳,就无法使用41bit存储下了怎么办呢?这实际上很简单,把存储IDLong类型改为容量更大的引用类型即可,也就是用更大的比特位来存放时间戳。

OK~,想明白上面的问题后,接着再聊聊分布式ID的重复问题,如果系统的并发较高,导致同一毫秒内需要生成多个ID怎么办呢?也就是时间戳位重复的情况下该怎么确保ID唯一性呢?其实在最后12bit上会存放一个顺序递增的序列值,212次幂为4096,也就意味着同一毫秒内可以生成4096个不同的ID值。

但似乎又出现了一个问题:当系统每毫秒的并发ID需求超出4096怎么办呢?Sharding-Sphere的做法是留到下个毫秒时间戳时再生成ID,基本只要你的业务不是持续性的超出4096这个阈值,Sharding-Sphere的雪花算法都是够用的,毕竟一秒409.6w并发量,相信能够从容应对各类业务。

但一般分布式系统中,都会采用集群的模式部署核心业务,如果使用雪花算法的节点存在多个,并且部署在不同的机器上,这会导致同一个毫秒时间戳内,出现不同的并发需求,之前说到的解决方案,由于自增序列是基于堆中的对象实现,不同机器存在多个堆空间,也就是每个节点之间都维护着各自的自增序列,因此集群环境下依旧会产生重复的分布式ID

为了解决这个问题,雪花算法生成分布式ID中,第43~53bit会用来存储工作进程ID,当一个服务采用了集群方案部署时,不同的节点配置不同的worker-id即可。因为worker-id不同,所以就算毫秒时间戳、自增序列号完全一致,依旧不会导致ID出现冲突,从而确保分布式ID的全局唯一性。

上述这个过程便是雪花算法的实现原理,基本上能够确保任何时刻的ID不会出现重复,而且是基于时间戳+自增序列实现的原因,因此也能够确保ID呈现自增性增长,从而避免索引树的频繁分裂。

3.3、Sharding-Sphere绑定表、广播表实践

   前面完成了最基本的库表数据分片,接着来聊一聊Sharding-Sphere中其他的一些表类型,如绑定表、广播表,这两种表类型也是实际业务中经常会需要使用的表类型,对于为何需要使用的缘故,在2.2.1、2.2.2阶段已经详细说明过了,这里就不再重复赘述。

3.3.1、绑定表配置实战

在之前创建表的时候,咱们创建了order、order_info两张表,这两张表十分具有代表性,因为订单表和订单详情表之间,存在明显的主外键关系,一笔订单可能结算多个商品,所以会生成多笔订单详情记录,订单数据和订单详情数据属于一对多的关系。

如果按照传统的分片规则,对两张表的数据做分发,这就很有可能导致一笔订单记录中,多笔订单详情记录被分发到不同的节点中存储,当需要通过关联订单表、订单详情表查询某笔订单数据时,就会出现跨库查询的情况。

为了避免上述问题产生,在Sharding-Sphere引入了一种绑定表的概念(MyCat中的ER表),专门用于处理存在主外键关系的多张表,接着做如下配置:

spring:shardingsphere:rules:sharding:tables:# 配置订单表的分片策略order:# 声明订单表所在的真实数据节点(ds0.order、ds1.order)actual-data-nodes: ds$->{0..1}.order# 配置分库规则database-strategy:standard:# 配置路由键为order_id(数据库中的列名)sharding-column: order_id# 配置分片算法(使用内置的取模分片算法)sharding-algorithm-name: key-int-mod# 配置订单表的主键生成策略key-generate-strategy:# 声明主键为order_idcolumn: order_id# 同样使用之前的雪花算法keygenerator-name: global-id-snowflake# 配置订单详情表的分片策略order_info:# 声明商品详情表所在的真实数据节点(ds0.order_info、ds1.order_info)actual-data-nodes: ds$->{0..1}.order_info# 配置分库规则database-strategy:standard:# 配置路由键为order_id(这里的路由键要和订单表一致)sharding-column: order_id# 配置分片算法(使用内置的取模分片算法)sharding-algorithm-name: key-int-mod# 配置订单详情表的主键生成策略key-generate-strategy:# 声明主键为order_info_idcolumn: order_info_id# 同样使用之前的雪花算法keygenerator-name: global-id-snowflake# 这里配置绑定表关系binding-tables:# 配置第一组绑定表的关系(订单表、订单详情表)- order,order_info
复制代码

首先将两张表的路由键和分片算法设为一致,接着最后通过binding-tables属性来设置一组绑定表即可,此时来做一个插入测试:

class OrderServiceImplTest extends DbShardingJdbcApplicationTests {@Autowiredprivate OrderService orderService;@Autowiredprivate OrderInfoService orderInfoService;/*** 测试绑定表的效果* **/@Testvoid orderOrOrderInfoInsert() {// 插入一条订单数据Order order = new Order();order.setUserId(111111L);order.setOrderPrice(100000);orderService.insertSelective(order);// 对同一笔订单插入三条订单详情数据for (int i = 1; i <= 3; i++) {OrderInfo orderInfo = new OrderInfo();// 前面插入订单的方法执行完成后会返回orderIDorderInfo.setOrderId(order.getOrderId());orderInfo.setShopingName("黄金1号竹子");orderInfo.setShopingPrice(8888);orderInfoService.insertSelective(orderInfo);}}
}
复制代码

执行上述代码后,数据库两张表的结果如下:


此时会发现,测试插入的这笔订单数据的三条订单详情,都会随着订单数据落入同一个库中,而且由于配置了绑定表的原因,后续基于这两张表做关联查询时,如果是通过order_id这个字段在做关联,Sharding-Sphere也只会查询一个库,而不会将所有的库全部做一次笛卡尔积查询。

3.3.2、广播表配置实战

Sharding-Sphere的广播表也就是MyCat中的全局表,主要是针对于一些所有库中都会用到的字典表使用的,例如系统菜单表、地区表、民族表、国籍表、职级表等,这种类型的表在所有库中经常被用于关联查询,因此可以将其直接配置为广播表,我这里以用户表为例,将其配置为一张广播表:

spring:shardingsphere:rules:sharding:tables:# 配置用户详情表的分片策略user_info:# 声明用户详情表所在的真实数据节点(ds0.user_info、ds1.user_info)actual-data-nodes: ds$->{0..1}.user_info# 配置用户详情表的主键生成策略key-generate-strategy:# 声明主键为user_idcolumn: user_id# 同样使用之前的雪花算法keygenerator-name: global-id-snowflake# 配置广播表信息broadcast-tables:- user_info
复制代码

此时注意上述的配置,其中并未指定数据的分片策略,仅在最后将user_info表配置成了广播表,接着来插入一些数据测试看看效果:

class UserInfoServiceImplTest extends DbShardingJdbcApplicationTests {@Autowiredprivate UserInfoService userInfoService;@Testvoid insertSelective() {// 插入三条性别为男的用户数据for (int i = 1; i <= 3; i++){UserInfo userInfo = new UserInfo();userInfo.setUserName("竹子" + i + "号");userInfo.setUserAge(18 + i);userInfo.setUserSex("男");userInfoService.insertSelective(userInfo);}// 插入两条性别为女的用户数据for (int i = 1; i <= 2; i++){UserInfo userInfo = new UserInfo();userInfo.setUserName("熊猫" + i + "号");userInfo.setUserAge(18 + i);userInfo.setUserSex("女");userInfoService.insertSelective(userInfo);}}
}
复制代码

上面插入了三条性别为男、两条性别为女的用户数据,接着来运行并看看数据库结果,如下:


此时会发现,虽然咱们未曾指定用户表的分片策略,但由于将其配制成了广播表,因此对该表的所有变更操作,都会落入到所有数据节点上执行,上图的两个库中,都有插入的5条用户数据。

也正因如此,所以无论是查询单条数据,还是查询多条数据,又或者是做关联查询,都可以在单库中完成,毕竟每个库中都具备完整的表数据。但如果变更较为频繁,或数据量较大的表,并不适合配制成广播表,因为广播表十分影响性能,需要等待所有节点插入完成后,才能向客户端返回结果。

3.4、Sharding-Sphere多种分片策略实践

   经过上述的学习后,咱们已经将Sharding-Sphere的基础用法玩明白了,在分库分表中最重要的就是路由键和分片算法,但Sharding-Sphere内置的一些分片算法,都仅是一些较为简单的分片算法,这使得咱们在很多场景中,无法满足特殊的业务需求。

2.3阶段中提到过,其实Sharding-Sphere中支持Inline、Standard、Complex、Hint这四种分片策略,而5.x版本中移除了原本的Inline策略,将其改进为自动化分片策略,也就是我们口中所谓的内置算法,对于一些简单的分片场景,可直接选用这种内置算法来处理。

针对于复杂度较高的业务场景,我们可以采用后续几种分片策略,来自定义数据分片的具体实现,以此提高Sharding-Sphere对复杂业务的支持性,接着咱们一起来简单聊一聊。

如果有玩过Sharding-Sphere4.x版本的小伙伴应该知道,在原先的版本中想要实现自定义分片策略,官方提供的SPI接口过于复杂,十分难理解,因此在5.x版本中做了优化:

  • 4.x中自定义Standard分片策略的SPI接口:
    • RangeShardingAlgorithm<~>:自定义范围查询分片策略时,需要实现的接口。
    • PreciseShardingAlgorithm<~>:实现精准查询分片策略时,需要实现的接口。
  • 5.x中自定义Standard分片策略的SPI接口:
    • StandardShardingAlgorithm:自定义精准查询、范围查询时,需要实现的接口。

Complex、Hint策略的接口依旧不变,是原有的老名字,即ComplexKeysShardingAlgorithm、HintShardingAlgorithm两个接口,接着来实际演练一下。

3.4.1、自定义Standard分片策略实战

这种分片策略只适用于范围查询和精确查询的场景,如BETWEEN AND、>、<、>=、<=等这类范围操作时,Sharding-Sphere的内置分片策略(Inline)模式下是不支持的,因此想要让你的程序支持这类范围查询操作,需要咱们手动编写对应的分片算法类,即使用Standard策略。

4.1以后的版本中,Inline模式下也支持范围查询操作,但需要手动开启相关支持,在InlineShardingStrategy中将allow-range-query-with-inline-sharding设置为true即可。

但为了版本兼容性,一般咱们都会选择自己实现Standard策略,撰写相关的实现类,接着做个简单的演示,首先需要在shardingAlgorithms属性下指定对应的分片算法实现类,格式如下:

sharding-algorithms:type: CLASS_BASEDprops:strategy: STANDARDalgorithmClassName: xxx
复制代码

接着基于shoping表做个实现,算法实现类如下:

// 商品表的Standard分库策略
public class ShopStandardSA implements StandardShardingAlgorithm {// 实现精确查询的方法(in、=查询会调用方法)@Overridepublic String doSharding(Collection collection, PreciseShardingValue psv) {// 获取逻辑表名:shopingString logicTableName = psv.getLogicTableName();// 获取路由键:psv.getColumnName()// 获取本次SQL语句中具体的路由键值long shopingID = (Long)psv.getValue();// 将获取到的long值转换为BigInteger数值BigInteger shopIdBI = BigInteger.valueOf(shopingID);// 通过获取到的ID值对2取模,计算出目标表的后缀BigInteger target = shopIdBI.mod(new BigInteger("2"));// 拼接上逻辑表名作为前缀,得到最终的目标表名String targetTable = logicTableName + "_0" + target;// 判断计算出的目标表是否在Logic_DB中存在if (collection.contains(targetTable))// 如果配置的数据节点中有这张表,则直接返回目标表名return targetTable;// 不存在则抛出相应的异常信息throw new UnsupportedOperationException(targetTable +"表在逻辑库中不存在,请检查你的SQL语句或数据节点配置...");}// 实现范围查询的方法(BETWEEN AND、>、<、>=、<=会调用的方法)@Overridepublic Collection<String> doSharding(Collection collection, RangeShardingValue rsv) {// 这里实现范围查询具体的处理逻辑....// 直接返回查询所有数据节点return collection;}@Overridepublic Properties getProps() {return null;}// 初始化方法@Overridepublic void init(Properties properties) {System.out.println("正在使用自定义的Standard分片算法......");}
}
复制代码

在上面的分片算法实现类中,实现了精准查询和范围查询的分片逻辑后,接着在yml文件中配置一下使用该算法类即可,如下:

spring:shardingsphere:# 配置分片规则rules:# 配置分片策略sharding:# 配置所有分片表tables:# 首先配置商品表的分片策略shoping:# 声明商品表所在的真实数据节点(把原本写死的表名改成表达式)actual-data-nodes: ds$->{0..1}.shoping_0$->{0..1}# 配置分库规则database-strategy:standard:# 配置路由键为shoping_id(数据库中的列名)sharding-column: shoping_id# 配置分片算法sharding-algorithm-name: db-inline-mod            # 配置分表规则table-strategy:standard:# 配置分表的路由键:商品名称sharding-column: shoping_id# 配置算法的实现方式指向自定义的算法类sharding-algorithm-name: shop-standard-shardingsharding-algorithms:# 配置一个自定义的Standard分片算法shop-standard-sharding:# 声明使用自定义的算法实现类type: CLASS_BASEDprops:# 声明分片策略strategy: STANDARD# 指明算法实现类(配置全限定名)algorithmClassName: com.zhuzi.dbshardingjdbc.shardingAlgorithm.ShopStandardSA
复制代码

通过上述这种方式就实现了最基本的standard的定义,但实际上Sharding-Sphere5.x中默认使用的即是standard分片策略,只不过之前咱们是通过行表达式和内置算法来配置分片规则,现在换成了自定义算法类来实现分片规则。

自定义Complex、Hint分片策略的步骤大致相同,先实现对应接口,重写里面的doSharding()方法,自己撰写逻辑返回对应的具体库或表,接着在yml中配置一下对应的分片类路径即可,这里就不再重复赘述,感兴趣的可自行实验~

最后简单说明一下Complex、Hint分片策略的适用场景:

  • Complex:适用于多路由键的场景,一张表需要通过多个核心字段查询时,可以配置多个路由键,此时就需要自己实现分片路由的算法。
  • Hint:当一张表经常需要执行一些较为复杂的SQL语句时,这种SQL语句Sharding-Sphere无法自动解析,就可以自己编写Hint策略的实现类,强制指定这些SQL落入到哪些节点中处理。

四、Sharding-Sphere框架总结

   上述一点点的从引入概念,到Sharding-Sphere的上手实战,携手诸位大致将分库分表实践操作进行了落地,其实相对来说也并不复杂,主要在于库表的分片规则一定要配置好,因为这是Sharding-Sphere的核心,也是大多数分库分表技术栈的核心。不过值得吐槽的一点是:Sharding-Sphere每个大版本之间,配置信息中的名称和样式都会存在不小差距,也正因如此,接触过之前版本的小伙伴在熟悉新版本时,会踩下很多坑~

但这点目前来说也不必太过担心,之前3.x、4.x系列中,因为整个生态还未彻底完善,所以每次大版本更新后,配置也会出现较大的变动,但目前的5.x系列趋向于稳定状态,因此学习好了5.x版本的配置,后续升级到更高的版本时,也不会出现太大的变动。

而且就算出现大变动也没关系,由于Sharding-Sphere是当当网贡献的原因,所以Apache官网的中文开发手册也特别详细,也不像Spring官网那样,有些地方翻译过来语义都存在偏差,Sharding-Sphere官网的开发手册,基本上有过开发经验的小伙伴都能读懂,撰写的较为细致。

但本文更多的是偏向于讲Sharding-Sphere-JDBC的水平分库,对于垂直分库却很少提及,这是由于稍具规模的项目都会采用分布式/微服务架构,本身每个核心业务服务都会具备独享库,因此也无需Sharding-Sphere来做垂直分库。

但本文中,还有一个较为重要、且常用的技术没有提及,那就是通过Sharding-Sphere来实现读写分离,但读写分离的实现依旧要靠客户端来实现,客户端在分发SQL时,将select操作发向从库,将insert、delete、update等操作发向主库,而Sharding-Sphere就支持实现客户端的读写SQL分发。

对于空缺的读写分离配置,其实和水平分库的配置也相差不大,配置多个数据源,然后配置好分发策略即可。

4.1、浅析Sharding-Sphere工作原理

   因为之前我用的是3.x版本,因此只翻阅过3.x系列的部分源码,这里也就不对Sharding-Sphere的工作原理做深入展开了,咱们这里就简单聊一聊Sharding-Sphere的工作原理,其核心工作步骤会分为如下几步:

  • 配置加载:在程序启动时,会读取用户的配置好的数据源、数据节点、分片规则等信息。
  • SQL解析:SQL执行时,会先根据配置的数据源来调用对应的解析器,然后对语句进行拆解。
  • SQL路由:拆解SQL后会从中得到路由键的值,接着会根据分片算法选择单或多个数据节点。
  • SQL改写:选择了目标数据节点后,接着会改写、优化用户的逻辑SQL,指向真实的库、表。
  • SQL执行:对于要在多个数据节点上执行的语句,内部开启多线程执行器异步执行每条SQL
  • 结果归并:持续收集每条线程执行完成后返回的结果集,最终将所有线程的结果集合并。
  • 结果处理:如果SQL中使用了order by、max()、count()...等操作,对结果处理后再返回。

整个Sharding-Sphere大致工作步骤如上,这个过程相对来说也比较简单,但具体的实现会比较复杂,针对于不同的数据库,内部都会实现不同的解析器,如MySQLMySQL的解析器,PgSQL也会有对应的解析器,同时还会做SQL语句做优化。而SQL路由时,除开要考虑最基本的数据分片算法外,还需要考虑绑定表、广播表等配置,来对具体的SQL进行路由

4.2、Sharding-JDBC/Porxy、MyCat区别

   本文虽说是讲Sharding-Sphere的教学,但主体内容更偏向于讲Sharding-JDBC,对于Sharding-Proxy却鲜有提及,其实这也是个人刻意为之的结果,Sharding-Proxy的用法基本上和Sharding-JDBC完全相同,不同的区别在于:Sharding-Proxy只是单独拧出来部署了而已

同时在前面我也曾提过一句,对于SpringBoot整合Sharding-JDBC框架,官方更加推荐使用application.properties的形式配置分库分表规则,包括官方文档中给出的配置示例,也是采用properties的方式,因为通过application.yml这种方式做配置,会需要解决一些额外的问题,但为何我在文中给出的所有配置都是基于yml形式的呢?

其实这是为了兼容Sharding-ProxySharding-Proxy中的分片规则就是采用yml的形式配置,因此JDBC中采用yml方式配置,当需要项目再引入Proxy做代理式分库分表时,就只需要将application.yml中的配置信息拷贝过去做轻微改动即可。

最后咱们再来简单对比一下Sharding-JDBC、Sharding-Porxy、MyCat三款产品之间的区别:

对比项Sharding-JDBCSharding-ProxyMyCat
性能开销较低较高
异构支持不支持支持支持
网络次数最少一次最少两次最少两次
异构语言仅支持Java支持异构支持异构
数据库支持MySQL、PgSQL任意数据库任意数据库
配置管理去中心化中心化中心化
部署方式依赖工程中间件中间件
业务侵入性较低
连接开销
事务支持XA、Base、Local事务同前者XA事务
功能丰富度一般
社区活跃性活跃活跃一言难尽
版本迭代性极低
多路由键支持221
集群部署支持支持支持
分布式序列雪花算法雪花算法自增序列

三款产品之间的大致区别如上,其实阿里云自带的DRDS这款产品,相对来说比两者更为全面,但还是那个道理,好用的产品都得收费,所以大家可根据业务自行做抉择,也包括选型时也可放眼于分布式数据库方向,如TiDB这类产品,而不仅仅将目光停留在传统的关系型数据库。


http://www.ppmy.cn/news/416927.html

相关文章

数据库-内容的查询 二

-- > any 比最小的值要高 select * from products where unitprice > any (select unitprice from products where categoryid 2) order by unitprice asc select min(unitprice) from products where categoryid 2 -- < any 比最大的值要小 select * from products…

[EPANET][翻译]认识EPANET对象集与度量衡

EPANET对象集&#xff08;物理对象与逻辑对象&#xff09; 注意&#xff1a;因本人英语水平有限&#xff0c;可能会有一些翻译不准确的问题&#xff0c;如有疑问&#xff0c;建议直接查看EPANET的用户手册(EN2Manual). EPANET包含了所有能在管网图上出现的物理对象&#xff0c;…

Oracle EBS 企业税改方案(三)-未结采购单据及部分AP Invoices税率调整

前提条件: Oracle EBS 企业税改方案(一)-业务需求整理及基础设置篇Oracle EBS系统版本:11.5.10未结采购单处理起来比未结销售订单麻烦多了,由于采购订单底层表架构及逻辑,如采购接收以后不会拆分行。特别是好多公司对PR、PBA、Quotation或标准PO等等有比深度定制,处理起来比较…

ps4移植android游戏,捡垃圾之路 篇八:这一次PSV用实力吊打Switch!2021你需要知道的安卓移植大作(含游戏下载)...

捡垃圾之路 篇八:这一次PSV用实力吊打Switch!2021你需要知道的安卓移植大作(含游戏下载) 2021-04-06 13:20:15 89点赞 463收藏 125评论 创作立场声明:PSV我已经坚持了很多年了,被各种大神开源之后发掘出来了非常多的玩法。本次热心分享,不喜勿喷,喷我就骂回去。喜欢的朋友…

psv连接电脑显示无法连接服务器,psv ftp服务器找不到

psv ftp服务器找不到 内容精选 换一换 介绍常见的安全组配置示例。如下示例中,出方向默认全通,仅介绍入方向规则配置方法。允许外部访问指定端口不同安全组内的弹性云服务器内网互通仅允许特定IP地址远程连接弹性云服务器SSH远程连接Linux弹性云服务器RDP远程连接Windows弹性…

psv登陆商店显示服务器,psv云服务器

psv云服务器 内容精选 换一换 华为云帮助中心&#xff0c;为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档&#xff0c;帮助您快速上手使用华为云服务。 将弹性云服务器移出云服务器组。移出后&#xff0c;该云服务器与云…

Java集合框架:栈、Stack详解

目录 一、栈 二、栈的使用 1. Stack类 2. 栈的模拟实现 三、栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环&#xff08;如&#xff1a;逆序打印链表&#xff09; 3. 栈的oj题练习&#xff08;oj题中都用到了栈这种数据结构&#xff09; 四、栈&#xff0c;虚拟机…

ATTCK v13版本战术介绍——凭证访问(三)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权、防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中凭证访问战术第13-17种子技术&#xff0c;后续会介绍凭证访问其他子技术&#xff0c;敬请关注。 二、ATT&…