引言

本文为社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

前面《MySQL主从原理篇》、《MySQL主从实践篇》两章中聊理解了MySQL主备读写别离、多主多写热备等计划,但假如这些高可用架构仍旧无法满意事务规划,或事务增加的需求,此刻就需求考虑选用分库分表架构。

在分库分表领域中,其实有许许多多的一些落地技能栈,如TDDL、TSharding、Sharding-Sphere、MyCat、Atlas、Oceanus、Vitess.....,但经时刻沉积与岁月洗礼后,现在干流的计划也就剩下了MyCat、Sharding-Sphere两种,MyCat近几年由于某些原因,开端逐步走下坡路,反观投入Apache怀有的Sharding-Sphere热度逐步上升,其现在的最新版别也相对安稳。

正是由于上述原因,现在越来越多的企业选用Sharding-Sphere作为分库分表的落地计划,因而《全解MySQL专栏》中的分库分表实践华章,相同会依据Sharding-Sphere这套技能来打开叙述~

一、初识Apache-Sharding-Sphere生态

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

Apache-Sharding-Sphere的前身是当当网开源的Sharding-JDBC结构,后边引进Zookeeper作为注册中心,又研发了Sharding-Proxy中间件,贡献给Apache软件基金会后,正式整组成Sharding-Sphere生态,并且支撑、兼容各种数据库,Apache-Sharding-Sphere官网上可看到的开展进程如下:
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

现在最新的5.2.1版别归于其间5.x阶段的一个版别,现在支撑可可拔插功用,其支撑的中心功用如下:
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

根本上关于《分库分表副作用篇》中聊到的各类问题,在该版别中都有对应的处理计划,如多表连查、数据分片、散布式事务、数据迁移与扩容、散布式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则支撑多数据源的办理,布置形状如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

Java-ORM结构在履行SQL句子时,Sharding-JDBC会以切面的办法阻拦发往数据库的句子,接着依据装备好的数据源、分片规矩和路由键,为SQL挑选一个方针数据源,然后再发往对应的数据库节点处理。

Sharding-JDBC在整个事务体系中对功用损耗极低,但为何后边又会推出Sharding-Proxy呢?由于Sharding-JDBC装备较为费事,比方在散布式体系中,任何运用分库分表的服务都需求独自装备多数据源地址、路由键、分片战略….等信息,一同它也仅支撑Java言语,当一个体系是用多言语异构的,此刻其他言语开发的子服务,则无法运用分库分表战略。

1.2、Sharding-Proxy中间件简介

也正是由于装备无法统一办理、不支撑异构体系的原因,后边又引进Sharding-Proxy来处理这两个问题,Sharding-Proxy能够将其理解成一个伪数据库,关于运用程序而言是完全通明的,它会以中间件的办法独立布置在体系中,布置形状如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

运用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做分库分表,布置形状如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

这种混合式的布置计划,一切的数据分片战略都会放到Zookeeper中统一办理,然后一切的子服务都去Zookeeper中拉取装备文件,这样就能很方便的依据事务状况,来灵敏的建立适用于各种场景的运用体系,这样也能够让数据源、分片战略、路由键….等装备信息灵敏,能够在线上动态修改装备信息,修改后能够在线上环境中动态感知。

Sharding-Sphere还提供了一种单机办法,即直接将数据分片装备放在Proxy中,但这种办法仅适用于开发环境,由于无法将分片装备同步给多个实例运用,也就意味着会导致其他实例由于感知不到装备改变,然后形成装备信息不共同的过错。

二、Sharding-Sphere中的中心概念

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

  • 路由键:也被称为分片键,也便是作为数据分片的基准字段,能够是一个或多个字段组成。
  • 分片算法:依据路由键做一定逻辑处理,然后核算出一个终究节点方位的算法。

举个例子来感触一下,比方按user_id将用户表数据分片,每八百万条数据区分一张表,那在这儿,user_id便是路由键,而按user_id做规划判别则归于分片算法,一张表中的一切数据都会依据这两个根底,后续对一切的读写SQL进行改写,然后定位到详细的库、表方位。

2.1、分库分表的作业流程

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

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、绑定表

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

在《分库分表后遗症-主外键约束》中聊到过,当多张表之间存在物理或逻辑上的主外键联系,假如无法保证同一主键值的外键数据落入同一节点,显然在查询时就会发生跨库查询,这无疑对功用影响是极大的,所以在其间也提到过能够运用绑定表的办法处理该问题。

比方前面事例中的order_id、order_info_id能够装备一组绑定表联系,这样就能够让订单概况数据跟着订单数据一同落库,简略的说便是:装备绑定表的联系后,外键的表数据会跟着主键的表数据落入同一个库中,这样在做主外键相关查询时,就能有用防止跨库查询的情形呈现。

2.2.2、播送表

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

在《分库分表后遗症-跨库Join问题》中相同聊到过,当有些表需求常常被用来做连表查询时,这种频繁相关查询的表,假如每次都走跨库Join,这显然又会形成一个令人头疼的功用问题,所以关于一些常常用来做相关查询的表,就能够将其装备为播送表,播送表在有些当地也被称为同步表、网络表、大局表,但本质上表达的意义都相同,如下:
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

播送表是一种会在一切库中都创立的表,以体系字典表为例,将其装备为播送表之后,向其增、删、改一条或多条数据时,一切的写操作都会发给悉数库履行,然后保证每个库中的表数据都共同,后续在需求做连表查询时,只需求相关本身库中的字典表即可,然后防止了跨库Join的问题呈现。

2.2.3、单表

单表的意义比较简略,并非一切的表都需求做分库分表操作,所以当一张表的数据无需分片到多个数据源中时,就可将其装备为单表,这样一切的读写操作终究都会落入这一张单表中处理。

2.2.4、动态表

动态表的概念在Sharding-Sphere最新的5.x文档中现已移除了,但也能够依据分片算法去结束,所以尽管移除了动态表的概念,但也能够结束相同的作用,动态表的概念是指表会跟着数据增加、或跟着时刻推移,不断的去创立新表,如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

我们是否还记得之前《库内分表篇》中的结束呢?其时为了处理单月数据增加过高的问题,我们手动结束了一套按月动态分表的计划,但在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的逻辑代码就在此先疏忽,这些我们都会的基操进程,就不写出来占用篇幅啦,后续会附上源码地址的,终究建立出的项目结构如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

尽管看起来代码不少,但俺也是经过工具快速生成的,根本上敲几下键盘~,到这儿为止,项目的根底结构就建立结束啦,后边开端实在的分库分表装备。

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: Standalone
      repository:
        type: JDBC
    # 装备多个数据源
    datasource:
      names: ds0,ds1
      # 装备第一个数据源
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: 「数据库节点1的地址」
        username: 「数据库节点1的账号」
        password: 「数据库节点1的暗码」
      # 装备第二个数据源
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: 「数据库节点2的地址」
        username: 「数据库节点1的账号」
        password: 「数据库节点1的暗码」

上述这拼装备中,需求经过names装备多个数据源的别号,接着需求为每个别号装备对应的数据源信息,依照上述办法编写好装备文件后,则表明结束了多数据源的装备。

3.2.2、多数据源可用性测验

为了保证多数据源的可用性,接着先简略装备一张表:

spring:
  shardingsphere:
    # 履行时显现SQL句子
    props:
      # 日志显现详细的SQL
      sql-show: true
    # 装备分片规矩
    rules:
      # 装备分片战略
      sharding:
        # 装备一切分片表
        tables:
          # 首要装备产品表的分片战略
          shoping:
            # 声明产品表地点的实在数据节点(这儿先显式声明一个节点测验)
            actual-data-nodes: ds0.shoping_00

然后编撰一个测验用例,来测验一下多数据源的装备是否有用:

@SpringBootTest
public class DbShardingJdbcApplicationTests {
    @Test
    void contextLoads() {
    }
}
// shoping产品表的测验类
class ShopingServiceImplTest extends DbShardingJdbcApplicationTests {
    @Autowired
    private ShopingService shopingService;
    // 测验数据刺进的办法
    @Test
    void 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-mod
        sharding-algorithms:
          # 装备前面的分库算法
          db-inline-mod:
            # 声明是 INLINE 简略类型的分片
            type: INLINE
            props:
              # 挑选对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表的数据看看,如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

很显然,成果与我们幻想的数据共同,但上述装备中的取模算法,也能够直接运用Sharding-Sphere内置的取模算法,装备办法如下:

sharding-algorithms:
  # 装备一个取模算法
  key-int-mod:
    # 运用ShardingSphere内置的取模算法
    type: MOD
    props:
      # 声明分库的节点数量
      sharding-count: 2

经过运用内置分片算法的办法去做取模也是能够的,官方内置了取模、哈希取模、时刻规划、数据规划、容量规划等多种简略的分片算法,详细的可参阅《ShardingSphere官网-分片算法》。

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_name
                sharding-algorithm-name: key-hash-mod
        sharding-algorithms:
          # 装备哈希取模的分表算法
          key-hash-mod:
            # 运用内置的哈希取模算法
            type: HASH_MOD
            props:
              # 声明分表的节点数量
              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);
    }
}

测验之前先将本来表中的数据清空,接着履行上述代码,数据库中的成果如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

此刻调查四张表中的数据,数据并未呈现重复,但刺进的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_price
    from 
        shoping
    where 
        shoping_id = ?
2022-11-25 16:38:22.334  INFO 15708 --- [main] ShardingSphere-SQL: Actual SQL: ds1 :::
    select 
        shoping_id, shoping_name, shoping_price
    from 
        shoping_00
    where 
        shoping_id = ? 
    UNION ALL 
        select 
            shoping_id, shoping_name, shoping_price
        from 
            shoping_01
        where 
            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);
}

履行成果如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

此刻我们会发现,尽管将前面刺进的一切数据都查询出来了,但显然没有了次序,这是由于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重复的问题呈现,尽管能够经过设置自增步长的办法处理,但这种办法对后续的扩容又不大友爱,因而在散布式场景中,急需一种既能保证大局仅有、又能保证次序递加的技能呈现,以此来处理ID重复这个扎手问题。

在早期的散布式体系中遇到该问题时,为了保证主键的仅有性,只能抛弃递加性,挑选无序的UUID来作为主键值,直到TwitterSnowflake雪花算法开源后,根本上雪花算法成为了散布式ID的干流计划,而关于该算法,MyBatis-Plus、Sharding-Sphere中都有内置的支撑,我们首要来做个简略装备:

spring:
  shardingsphere:
    # 装备分片规矩
    rules:
      # 装备分片战略
      sharding:
        # 装备一切分片表
        tables:
          # 首要装备产品表的分片战略
          shoping:
            # 装备shoping表的主键生成战略
            key-generate-strategy:
              # 声明主键为shoping_id
              column: shoping_id
              # 相同指向global-id-snowflake这个详细的主键生成战略
              keygenerator-name: global-id-snowflake
        key-generators:
          # 装备上面的主键生成战略
          global-id-snowflake:
            # 挑选运用内置的雪花算法
            type: SNOWFLAKE
            props:
              # 分配一个作业节点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值了,接着先将本来库中的表数据清空,然后运转后表的数据如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

图中圈出的一连串数字,即是Sharding-Sphere为我们生成的散布式ID,那终究雪花算法是如何保证大局仅有性的呢?接下来一同深化聊聊雪花算法的结束原理。

3.2.8、Snowflake雪花算法的结束原理

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

雪花算法生成的散布式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_id
              column: 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_id
              column: order_info_id
              # 相同运用之前的雪花算法
              keygenerator-name: global-id-snowflake
        # 这儿装备绑定表联系
        binding-tables:
          # 装备第一组绑定表的联系(订单表、订单概况表)
          - order,order_info

首要将两张表的路由键和分片算法设为共同,接着终究经过binding-tables属性来设置一组绑定表即可,此刻来做一个刺进测验:

class OrderServiceImplTest extends DbShardingJdbcApplicationTests {
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderInfoService orderInfoService;
    /**
     * 测验绑定表的作用
     * **/
    @Test
    void 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();
            // 前面刺进订单的办法履行结束后会回来orderID
            orderInfo.setOrderId(order.getOrderId());
            orderInfo.setShopingName("黄金1号竹子");
            orderInfo.setShopingPrice(8888);
            orderInfoService.insertSelective(orderInfo);
        }
    }
}

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

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

此刻会发现,测验刺进的这笔订单数据的三条订单概况,都会跟着订单数据落入同一个库中,并且由于装备了绑定表的原因,后续依据这两张表做相关查询时,假如是经过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_id
              column: user_id
              # 相同运用之前的雪花算法
              keygenerator-name: global-id-snowflake
        # 装备播送表信息
        broadcast-tables:
          - user_info

此刻留意上述的装备,其间并未指定数据的分片战略,仅在终究将user_info表装备成了播送表,接着来刺进一些数据测验看看作用:

class UserInfoServiceImplTest extends DbShardingJdbcApplicationTests {
    @Autowired
    private UserInfoService userInfoService;
    @Test
    void 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);
        }
    }
}

上面刺进了三条性别为男、两条性别为女的用户数据,接着来运转并看看数据库成果,如下:

(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!

此刻会发现,尽管我们未曾指定用户表的分片战略,但由于将其配制成了播送表,因而对该表的一切变更操作,都会落入到一切数据节点上履行,上图的两个库中,都有刺进的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_BASED
    props:
      strategy: STANDARD
        algorithmClassName: xxx

接着依据shoping表做个结束,算法结束类如下:

// 产品表的Standard分库战略
public class ShopStandardSA implements StandardShardingAlgorithm {
    // 结束准确查询的办法(in、=查询会调用办法)
    @Override
    public String doSharding(Collection collection, PreciseShardingValue psv) {
        // 获取逻辑表名:shoping
        String 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、>、<、>=、<=会调用的办法)
    @Override
    public Collection<String> doSharding(Collection collection, RangeShardingValue rsv) {
        // 这儿结束规划查询详细的处理逻辑....
        // 直接回来查询一切数据节点
        return collection;
    }
    @Override
    public Properties getProps() {
        return null;
    }
    // 初始化办法
    @Override
    public 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-sharding
        sharding-algorithms:
          # 装备一个自界说的Standard分片算法
          shop-standard-sharding:
            # 声明运用自界说的算法结束类
            type: CLASS_BASED
              props:
                # 声明分片战略
                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来结束读写别离,尽管在之前的《MySQL主从实践篇》中,我们从零开端建立出了一套MySQL主从架构,但读写别离的结束仍旧要靠客户端来结束,客户端在分发SQL时,将select操作发向从库,将insert、delete、update等操作发向主库,而Sharding-Sphere就支撑结束客户端的读写SQL分发。

关于空缺的读写别离装备,在《MySQL系列》结束后,我会抽时刻回来补齐,其实和水平分库的装备也相差不大,装备多个数据源,然后装备好分发战略即可。

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进行路由。

相同,关于Sharding-Sphere原理的深化剖析,这儿也先“赊一下账”,后续时刻富余后,自己先大致摸一遍源码后,再开一篇新的《Sharding-Sphere源码分析篇》来补齐空缺。

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-JDBC Sharding-Proxy MyCat
功用开支 较低 较高
异构支撑 不支撑 支撑 支撑
网络次数 最少一次 最少两次 最少两次
异构言语 仅支撑Java 支撑异构 支撑异构
数据库支撑 MySQL、PgSQL 恣意数据库 恣意数据库
装备办理 去中心化 中心化 中心化
布置办法 依靠工程 中间件 中间件
事务侵入性 较低
衔接开支
事务支撑 XA、Base、Local事务 同前者 XA事务
功用丰富度 一般
社区活泼性 活泼 活泼 一言难尽
版别迭代性 极低
多路由键支撑 2 2 1
集群布置 支撑 支撑 支撑
散布式序列 雪花算法 雪花算法 自增序列

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

终究,如若诸位对本文中的分库分表事例感兴趣,这儿也附上源码地址:《GitHub-ShardingSphere分库分表事例》,其间也包含了Sharding-Sphere5.2.1版别的官方开发手册-PDF版,究竟官网拜访起来速度并不那么可观,因而我顺手上传了一份PDF文档,里边包含了Sharding-Sphere5.2.1一切功用与技能细节,有需求的可在PDF目录下查找。