dynamic-datasource+shardingsphere-jdbc实现分库分表

项目背景

spring bootdynamic-datasource 实现了数据库的读写分离的基础上,增加分库分表的功能。

一、框架版本

1. 关键技术栈版本

    <properties>
        <java.version>8</java.version>
        <snakeyaml.version>1.33</snakeyaml.version>
    </properties>
    <dependencies>
        <!-- druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.20</version>
        </dependency>
        <!--dynamic-datasource-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.6.1</version>
        </dependency>
        <!-- shardingsphere-jdbc -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core</artifactId>
            <version>5.4.1</version>
        </dependency>
    </dependencies>

2. 补充说明

1. snakeyaml版本

  • 要求使用1.33版本以上
  • spring boot可以通过properties进行配置<snakeyaml.version>1.33</snakeyaml.version>

2. shardingsphere-jdbc-core-spring-boot-startershardingsphere-jdbc-core选型

  • 5.2.1版本之后修改了配置方式,不再单独提供spring boot版本依赖,统一使用shardingsphere-jdbc-core
  • shardingsphere-jdbc-core-spring-boot-starter:配置文件写在application.yml
  • shardingsphere-jdbc-core:使用专门的驱动类,配置文件写在单独的yaml文件里

3. 最新版本Maven仓库链接

二、配置文件

1. spring boot配置文件:application.yml

spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        sharding:
          url: jdbc:shardingsphere:classpath:sharding-sphere-config.yaml
          driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver

2. shardingsphere-jdbc配置文件

mode:
  type: Standalone
  repository:
    type: JDBC

dataSources:
  write:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
  read:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root

rules:
  - !SHARDING
    autoTables:
      sharding_test:
        actualDataSources: write
        keyGenerateStrategy:
          column: id
          keyGeneratorName: snowflake
        shardingStrategy:
          standard:
            shardingColumn: id
            shardingAlgorithmName: auto_mod

    shardingAlgorithms:
      auto_mod:
        type: MOD
        props:
          sharding-count: 3

    keyGenerators:
      snowflake:
        type: SNOWFLAKE

  - !READWRITE_SPLITTING
    dataSources:
      write:
        writeDataSourceName: write
        readDataSourceNames:
          - read
        loadBalancerName: round_robin
    loadBalancers:
      round_robin:
        type: ROUND_ROBIN

props:
  sql-show: true

四、配置详解

1. 数据源配置

(1) 配置 DataSource Driver

方式一: 使用spring boot默认数据库配置
spring:
  datasource:
    # 配置 DataSource Driver
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    # 指定 YAML 配置文件
    url: jdbc:shardingsphere:classpath:xxx.yaml
方式二: 使用dynamic-datasource配置数据库
spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver
        sharding:
          url: jdbc:shardingsphere:classpath:sharding-sphere-config.yaml
          driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver

(2) 配置Yaml

背景信息

ShardingSphere-JDBC 支持所有的数据库 JDBC 驱动和连接池。

示例的数据库驱动为 MySQL,连接池为 HikariCP,可以更换为其他数据库驱动和连接池。当使用 ShardingSphere-JDBC 时,JDBC 池的属性名取决于各自 JDBC 池自己的定义,并不由 ShardingSphere 定义,相关的处理可以参考类org.apache.shardingsphere.infra.datasource.pool.creator.DataSourcePoolCreator。例如对于 Alibaba Druid 1.2.9 而言,使用 url 代替如下示例中的 jdbcUrl 是预期行为。

参数解释
dataSources: # 数据源配置,可配置多个 <data-source-name>
  <data_source_name>: # 数据源名称
    dataSourceClassName: # 数据源完整类名
    driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
    jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
    username: # 数据库用户名,以数据库连接池自身配置为准
    password: # 数据库密码,以数据库连接池自身配置为准
    # ... 数据库连接池的其它属性
配置示例
dataSources:
  write:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
  read:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dirk?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root

2. 规则配置

(1) 基本概念

逻辑表

相同结构的水平拆分数据库(表)的逻辑名称,是 SQL 中表的逻辑标识。 例:订单数据根据主键尾数拆分为 10 张表,分别是 t_order_0t_order_9,他们的逻辑表名为 t_order

真实表

在水平拆分的数据库中真实存在的物理表。 即上个示例中的 t_order_0t_order_9

绑定表

指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。 例如:t_order 表和 t_order_item 表,均按照 order_id 分片,并且使用 order_id 进行关联,则此两张表互为绑定表关系。 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。 举例说明,如果 SQL 为:

SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在不配置绑定表关系时,假设分片键 order_id 将数值 10 路由至第 0 片,将数值 11 路由至第 1 片,那么路由后的 SQL 应该为 4 条,它们呈现为笛卡尔积:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在配置绑定表关系,并且使用 order_id 进行关联后,路由的 SQL 应该为 2 条:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

其中 t_order 表由于指定了分片条件,ShardingSphere 将会以它作为整个绑定表的主表。 所有路由计算将会只使用主表的策略,那么 t_order_item 表的分片计算将会使用 t_order 的条件。

注意:绑定表中的多个分片规则,需要按照逻辑表前缀组合分片后缀的方式进行配置,例如:

rules:
- !SHARDING
  tables:
    t_order:
      actualDataNodes: ds_${0..1}.t_order_${0..1}
    t_order_item:
      actualDataNodes: ds_${0..1}.t_order_item_${0..1}
广播表

指所有的数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。

单表

指所有的分片数据源中仅唯一存在的表。 适用于数据量不大且无需分片的表。

注意:符合以下条件的单表会被自动加载:

  • 数据加密、数据脱敏等规则中显示配置的单表
  • 用户通过 ShardingSphere 执行 DDL 语句创建的单表

其余不符合上述条件的单表,ShardingSphere 不会自动加载,用户可根据需要配置单表规则进行管理。


分片键

用于将数据库(表)水平拆分的数据库字段。 例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL 中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,Apache ShardingSphere 也支持根据多个字段进行分片。

分片算法

用于将数据分片的算法,支持 =>=<=><BETWEENIN 进行分片。 分片算法可由开发者自行实现,也可使用 Apache ShardingSphere 内置的分片算法语法糖,灵活度非常高。

自动化分片算法

分片算法语法糖,用于便捷的托管所有数据节点,使用者无需关注真实表的物理分布。 包括取模、哈希、范围、时间等常用分片算法的实现。

自定义分片算法

提供接口让应用开发者自行实现与业务实现紧密相关的分片算法,并允许使用者自行管理真实表的物理分布。 自定义分片算法又分为:

  • 标准分片算法

用于处理使用单一键作为分片键的 =INBETWEEN AND><>=<= 进行分片的场景。

  • 复合分片算法

用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。

  • Hint 分片算法

用于处理使用 Hint 行分片的场景。

分片策略

包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。 真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。

强制分片路由

对于分片字段并非由 SQL 而是其他外置条件决定的场景,可使用 SQL Hint 注入分片值。 例:按照员工登录主键分库,而数据库中并无此字段。 SQL Hint 支持通过 Java API 和 SQL 注释两种方式使用。 详情请参见强制分片路由。


行表达式

行表达式是为了解决配置的简化与一体化这两个主要问题。在繁琐的数据分片规则配置中,随着数据节点的增多,大量的重复配置使得配置本身不易被维护。 通过行表达式可以有效地简化数据节点配置工作量。

对于常见的分片算法,使用 Java 代码实现并不有助于配置的统一管理。 通过行表达式书写分片算法,可以有效地将规则配置一同存放,更加易于浏览与存储。

行表达式作为字符串由两部分组成,分别是字符串开头的对应 SPI 实现的 Type Name 部分和表达式部分。 以 <GROOVY>t_order_${1..3} 为例,字符
<GROOVY> 部分的子字符串 GROOVY 为此行表达式使用的对应 SPI 实现的 Type Name,其被 <> 符号包裹来识别。而字符串 t_order_${1..3}
为此行表达式的表达式部分。当行表达式不指定 Type Name 时,例如 t_order_${1..3},行表示式默认将使用 InlineExpressionParser SPI 的
GROOVY 实现来解析表达式。

以下部分介绍 GROOVY 实现的语法规则。

行表达式的使用非常直观,只需要在配置中使用 ${ expression }$->{ expression } 标识行表达式即可。 目前支持数据节点和分片算法这两个部分的配置。 行表达式的内容使用的是 Groovy 的语法,Groovy 能够支持的所有操作,行表达式均能够支持。 例如:

${begin..end} 表示范围区间
${[unit1, unit2, unit_x]} 表示枚举值

行表达式中如果出现连续多个 ${ expression }$->{ expression } 表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。

例如,以下行表达式:

${['online', 'offline']}_table${1..3}

最终会解析为:

online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3

分布式主键

传统数据库软件开发中,主键自动生成技术是基本需求。而各个数据库对于该需求也提供了相应的支持,比如 MySQL 的自增键,Oracle 的自增序列等。 数据分片后,不同数据节点生成全局唯一主键是非常棘手的问题。同一个逻辑表内的不同实际表之间的自增键由于无法互相感知而产生重复主键。 虽然可通过约束自增主键初始值和步长的方式避免碰撞,但需引入额外的运维规则,使解决方案缺乏完整性和可扩展性。

目前有许多第三方解决方案可以完美解决这个问题,如 UUID 等依靠特定算法自生成不重复键,或者通过引入主键生成服务等。为了方便用户使用、满足不同用户不同使用场景的需求, Apache ShardingSphere 不仅提供了内置的分布式主键生成器,例如 UUID、SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。

(2) 配置yaml

背景信息

数据分片 YAML 配置方式具有非凡的可读性,通过 YAML 格式,能够快速地理解分片规则之间的依赖关系,ShardingSphere 会根据 YAML 配置,自动完成 ShardingSphereDataSource 对象的创建,减少用户不必要的编码工作。

参数解释
rules:
- !SHARDING
  tables: # 数据分片规则配置
    <logic_table_name> (+): # 逻辑表名称
      actualDataNodes (?): # 由数据源名 + 表名组成(参考 Inline 语法规则)
      databaseStrategy (?): # 分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 分片算法名称
        complex: # 用于多分片键的复合分片场景
          shardingColumns: # 分片列名称,多个列以逗号分隔
          shardingAlgorithmName: # 分片算法名称
        hint: # Hint 分片策略
          shardingAlgorithmName: # 分片算法名称
        none: # 不分片
      tableStrategy: # 分表策略,同分库策略
      keyGenerateStrategy: # 分布式序列策略
        column: # 自增列名称,缺省表示不使用自增主键生成器
        keyGeneratorName: # 分布式序列算法名称
      auditStrategy: # 分片审计策略
        auditorNames: # 分片审计算法名称
          - <auditor_name>
          - <auditor_name>
        allowHintDisable: true # 是否禁用分片审计hint
  autoTables: # 自动分片表规则配置
    t_order_auto: # 逻辑表名称
      actualDataSources (?): # 数据源名称
      shardingStrategy: # 切分策略
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 自动分片算法名称
  bindingTables (+): # 绑定表规则列表
    - <logic_table_name_1, logic_table_name_2, ...> 
    - <logic_table_name_1, logic_table_name_2, ...> 
  defaultDatabaseStrategy: # 默认数据库分片策略
  defaultTableStrategy: # 默认表分片策略
  defaultKeyGenerateStrategy: # 默认的分布式序列策略
  defaultShardingColumn: # 默认分片列名称
  
  # 分片算法配置
  shardingAlgorithms:
    <sharding_algorithm_name> (+): # 分片算法名称
      type: # 分片算法类型
      props: # 分片算法属性配置
      # ...
  
  # 分布式序列算法配置
  keyGenerators:
    <key_generate_algorithm_name> (+): # 分布式序列算法名称
      type: # 分布式序列算法类型
      props: # 分布式序列算法属性配置
      # ...
  # 分片审计算法配置
  auditors:
    <sharding_audit_algorithm_name> (+): # 分片审计算法名称
      type: # 分片审计算法类型
      props: # 分片审计算法属性配置
      # ...

- !BROADCAST
  tables: # 广播表规则列表
    - <table_name>
    - <table_name>
操作步骤
  1. 在 YAML 文件中配置数据分片规则,包含数据源、分片规则、全局属性等配置项;
  2. 调用 YamlShardingSphereDataSourceFactory 对象的 createDataSource 方法,根据 YAML 文件中的配置信息创建 ShardingSphereDataSource。
配置示例

数据分片 YAML 配置示例如下:

rules:
  - !SHARDING
    autoTables:
      sharding_test:
        actualDataSources: write
        keyGenerateStrategy:
          column: id
          keyGeneratorName: snowflake
        shardingStrategy:
          standard:
            shardingColumn: id
            shardingAlgorithmName: auto_mod

    shardingAlgorithms:
      auto_mod:
        type: MOD
        props:
          sharding-count: 3

    keyGenerators:
      snowflake:
        type: SNOWFLAKE

  - !READWRITE_SPLITTING
    dataSources:
      write:
        writeDataSourceName: write
        readDataSourceNames:
          - read
        loadBalancerName: round_robin
    loadBalancers:
      round_robin:
        type: ROUND_ROBIN

通过 YamlShardingSphereDataSourceFactory 的 createDataSource 方法,读取 YAML 配置完成数据源的创建。

YamlShardingSphereDataSourceFactory.createDataSource(getFile("/META-INF/sharding-databases-tables.yaml"));

3. 其他配置

(1) 模式配置

参数解释
mode (?): # 不配置则默认单机模式
  type: # 运行模式类型。可选配置:Standalone、Cluster
  repository (?): # 持久化仓库配置
配置示例
mode:
  type: Standalone # 单机模式
  repository:
    type: JDBC

(2) props配置

背景信息

Apache ShardingSphere 提供属性配置的方式配置系统级配置。

参数解释
名称数据类型说明默认值
sql-show (?)boolean是否在日志中打印 SQL
打印 SQL 可以帮助开发者快速定位系统问题。日志内容包含:逻辑 SQL,真实 SQL 和 SQL 解析结果。
如果开启配置,日志将使用 Topic ShardingSphere-SQL,日志级别是 INFO
false
sql-simple (?)boolean是否在日志中打印简单风格的 SQLfalse
kernel-executor-size (?)int用于设置任务处理线程池的大小
每个 ShardingSphereDataSource 使用一个独立的线程池,同一个 JVM 的不同数据源不共享线程池
infinite
max-connections-size-per-query (?)int一次查询请求在每个数据库实例中所能使用的最大连接数1
check-table-metadata-enabled (?)boolean在程序启动和更新时,是否检查分片元数据的结构一致性false
操作步骤

属性配置直接配置在 ShardingSphere-JDBC 所使用的配置文件中,格式如下:

props:
    sql-show: true
配置示例
props:
  # 打印sql
  sql-show: true 
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
好的,我可以为您解答关于SpringBootShardingSphere实现读写分离的问题。 首先,您需要在pom.xml文件中添加ShardingSphere相关依赖: ```xml <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-core</artifactId> <version>${sharding-sphere.version}</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${sharding-sphere.version}</version> </dependency> ``` 其中${sharding-sphere.version}为ShardingSphere的版本号。 接下来,您需要在application.yml或application.properties中配置ShardingSphere的数据源和规则,例如: ```yaml spring: shardingsphere: datasource: names: ds_master, ds_slave_0, ds_slave_1 ds_master: url: jdbc:mysql://localhost:3306/mydb_master?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root ds_slave_0: url: jdbc:mysql://localhost:3306/mydb_slave_0?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root ds_slave_1: url: jdbc:mysql://localhost:3306/mydb_slave_1?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root sharding: default-data-source-name: ds_master master-slave-rules: ds_0: master-data-source-name: ds_master slave-data-source-names: ds_slave_0, ds_slave_1 load-balance-algorithm-type: round_robin ``` 以上配置中,我们配置了3个数据源:ds_master, ds_slave_0和ds_slave_1,其中ds_master为主库,ds_slave_0和ds_slave_1为从库。然后我们使用了ShardingSphere提供的master-slave规则将ds_master和ds_slave_0、ds_slave_1进行了关联,并使用了轮询算法进行负载均衡,从而实现了读写分离。 最后,您需要在SpringBoot主类上添加@EnableSharding注解,以启用ShardingSphere的功能。 这就是使用SpringBootShardingSphere实现读写分离的基本步骤。希望对您有所帮助!
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值