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

引言

作者:竹子爱熊猫

前面《MySQL主从原理篇》[1]、《MySQL主从实践篇》[2]两章中聊明白了MySQL主备读写分离、多主多写热备等方案,但如果这些高可用架构依旧无法满足业务规模,或业务增长的需要,此时就需要考虑选用分库分表架构。

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

正是由于上述原因,如今越来越多的企业选用Sharding-Sphere作为分库分表的落地方案,因此《全解MySQL专栏》[3]中的分库分表实践篇章,同样会基于Sharding-Sphere这套技术来展开叙述~

一、初识Apache-Sharding-Sphere生态

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

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

基本上对于《分库分表副作用篇》[5]中聊到的各类问题,在该版本中都有对应的解决方案,如多表连查、数据分片、分布式事务、数据迁移与扩容、分布式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、绑定表

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

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

2.2.2、广播表

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

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

2.2.3、单表

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

2.2.4、动态表

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

大家是否还记得之前《库内分表篇》[8]中的实现呢?当时为了处理单月数据增长过高的问题,咱们手动实现了一套按月动态分表的方案,但在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中加入如下依赖即可:

<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如下:

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;


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;


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:
    
    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:
    
    props:
      
      sql-show: true

    
    rules:
      
      sharding:
        
        tables:
          
          shoping:
            
            actual-data-nodes: ds0.shoping_00

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

@SpringBootTest
public class DbShardingJdbcApplicationTests {
    @Test
    void contextLoads() {
    }
}


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:
                
                sharding-column: shoping_id
                
                sharding-algorithm-name: db-inline-mod
        
        sharding-algorithms:
          
          db-inline-mod:
            
            type: INLINE
            props:
              
              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:
    
    type: MOD
    props:
      
      sharding-count: 2

通过使用内置分片算法的形式去做取模也是可以的,官方内置了取模、哈希取模、时间范围、数据范围、容量范围等多种简单的分片算法,具体的可参考《ShardingSphere官网-分片算法》[9]。

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);
    }
}

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

此时观察四张表中的数据,数据并未出现重复,但插入的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);
}

执行结果如下:

此时大家会发现,虽然将前面插入的所有数据都查询出来了,但显然没有了顺序,这是因为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主键唯一性问题》[10]时讲到过:

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

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

spring:
  shardingsphere:
    
    rules:
      
      sharding:
        
        tables:
          
          shoping:
            
            key-generate-strategy:
              
              column: shoping_id
              
              keygenerator-name: global-id-snowflake

        key-generators:
          
          global-id-snowflake:
            
            type: SNOWFLAKE
            props:
              
              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:
            
            actual-data-nodes: ds$->{0..1}.order

            
            database-strategy:
              standard:
                
                sharding-column: order_id
                
                sharding-algorithm-name: key-int-mod

            
            key-generate-strategy:
              
              column: order_id
              
              keygenerator-name: global-id-snowflake

          
          order_info:
            
            actual-data-nodes: ds$->{0..1}.order_info

            
            database-strategy:
              standard:
                
                sharding-column: order_id
                
                sharding-algorithm-name: key-int-mod

            
            key-generate-strategy:
              
              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();
            
            orderInfo.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:
            
            actual-data-nodes: ds$->{0..1}.user_info

            
            key-generate-strategy:
              
              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);
        }
    }
}

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

此时会发现,虽然咱们未曾指定用户表的分片策略,但由于将其配制成了广播表,因此对该表的所有变更操作,都会落入到所有数据节点上执行,上图的两个库中,都有插入的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表做个实现,算法实现类如下:

public class ShopStandardSA implements StandardShardingAlgorithm {

    
    @Override
    public String doSharding(Collection collection, PreciseShardingValue psv) {
        
        String logicTableName = psv.getLogicTableName();
        
        
        long shopingID = (Long)psv.getValue();
        
        BigInteger shopIdBI = BigInteger.valueOf(shopingID);
        
        BigInteger target = shopIdBI.mod(new BigInteger("2"));
        
        String targetTable = logicTableName + "_0" + target;
        
        if (collection.contains(targetTable))
            
            return targetTable;
        
        throw new UnsupportedOperationException(targetTable +
                "表在逻辑库中不存在,请检查你的SQL语句或数据节点配置...");
    }

    
    @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:
                
                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:
          
          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主从实践篇》[11]中,咱们从零开始搭建出了一套MySQL主从架构,但读写分离的实现依旧要靠客户端来实现,客户端在分发SQL时,将select操作发向从库,将insert、delete、update等操作发向主库,而Sharding-Sphere就支持实现客户端的读写SQL分发。

对于空缺的读写分离配置,在《MySQL系列》[12]完结后,我会抽时间回来补齐,其实和水平分库的配置也相差不大,配置多个数据源,然后配置好分发策略即可。

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

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

最后,如若诸位对本文中的分库分表案例感兴趣,这里也附上源码地址:《GitHub-ShardingSphere分库分表案例》[13],其中也包含了Sharding-Sphere5.2.1版本的官方开发手册-PDF版,毕竟官网访问起来速度并不那么可观,因此我顺手上传了一份PDF文档,里面涵盖了Sharding-Sphere5.2.1所有功能与技术细节,有需要的可在PDF目录下查找。

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Sharding-Proxy 是一个开源的分库中间件,它可以帮助应用程序实现无感知的分库操作。下面是一个简单的步骤来利用 Sharding-Proxy 进行分库: 1. 安装和配置 Sharding-Proxy:首先,你需要下载 Sharding-Proxy 的安装包,并解压到你的服务器上。然后,根据你的需求修改配置文件,配置数据源和分片规则等信息。 2. 创建数据库和:在进行分库之前,你需要创建相应的数据库和结构。你可以选择手动创建,或者使用 Sharding-Proxy 提供的自动建功能。 3. 配置分片规则:在 Sharding-Proxy 的配置文件中,你需要定义分片规则,指定如何将数据分散到不同的数据库和中。可以使用基于范围、哈希、精确等多种分片算法。 4. 连接到 Sharding-Proxy:在应用程序中,需要修改数据库连接信息,将原来连接数据库的地址改为连接 Sharding-Proxy 的地址。这样应用程序就可以通过 Sharding-Proxy 访问分片后的数据。 5. 进行分库操作:现在你可以在应用程序中执行正常的数据库操作,而无需关心具体的分库细节。Sharding-Proxy 会根据配置的规则自动将数据路由到正确的库和中。 需要注意的是,使用 Sharding-Proxy 进行分库操作需要仔细考虑数据一致性、事务处理、跨库查询等问题。在配置和使用过程中,建议参考官方文档和示例来确保正确性和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术小羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值