Sharding-Sphere系列-主从配置和分库分表

这篇具有很好参考价值的文章主要介绍了Sharding-Sphere系列-主从配置和分库分表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

主从配置和分库分表

Sharding-Sphere组成

Sharding-JDBC

Sharding-Proxy

Sharding-Sidecar(TODO)

Sharding-JDBC表的概念

逻辑表

广播表

绑定表

Sharding-JDBC中的分片策略

自动分片算法

标准分片算法

复合分片算法

自定义分片算法

分布式序列算法

Sharding-Sphere实战

shardingsphere的sql日志无法打印问题

配置的雪花算法不生效

Field 'brand_id' doesn't have a default value

Insert statement does not support sharding table routing to multiple data nodes

No database route info

主从分离和分库分表配置(修正后)

QueryWrapper和LambdaQueryWrapper

执行testSave()和findByBrandStatus()测试

一个小插曲


Sharding-Sphere组成

Sharding-JDBC 最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为 ShardingSphere,2020年4⽉16⽇正式成为 Apache 软件基⾦会的顶级项⽬。

随着版本的不断更迭 ShardingSphere 的核心功能也变得多元化起来。如图7-1,ShardingSphere生态包含三款开源分布式数据库中间件解决方案,Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar。

Apache ShardingSphere 5.x 版本开始致力于提供可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及对 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,而且仍在不断增加中。

Sharding-JDBC

Sharding-Proxy

Sharding-Sidecar

数据库

任意

MySQL

MySQL

连接消耗数

异构语言

JAVA 

任意

任意

性能

损耗低

损耗略高

损耗低

无中心化

静态入口

Sharding-JDBC

Sharding-JDBC是比较常用的一个组件,它定位的是一个增强版的JDBC驱动,简单来说就是在应用端来完成数据库分库分表相关的路由和分片操作,也是我们本阶段重点去分析的组件。

我们在项目内引入Sharding-JDBC的依赖,我们的业务代码在操作数据库的时候,就会通过Sharding-JDBC的代码连接到数据库。也就是分库分表的一些核心动作,比如SQL解析,路由,执行,结果处理,都是由它来完成的,它工作在客户端。Sharding-JDBC是对原有JDBC驱动的增强,在分库分表的场景中,为应用提供了如图所示的功能。

数据分片

分布式事务

数据库治理

分库分表

标准化事务接口

配置动态化

读写分离

XA强一致事务

编排治理

分片策略定制化

柔性事务

数据脱敏

无中心化分布式主键

可视化链路追踪

Sharding-Proxy

Sharding-Proxy有点类似于Mycat,它是提供了数据库层面的代理,什么意思呢?简单来说,以前我们的应用是直连数据库,引入了Sharding-Proxy之后,我们的应用是直连Sharding-Proxy,然后Sharding-Proxy通过处理之后再转发到mysql中。

这种方式的好处在于,用户不需要感知到分库分表的存在,相当于正常访问mysql。目前Sharding-Proxy支持Mysql和PostgreSQL两种数据库协议

Sharding-Sidecar(TODO)

看到Sidecar,大家应该就能想到服务网格架构,它主要定位于 Kubernetes 的云原生数据库代理,以 Sidecar 的形式代理所有对数据库的访问。目前Sharding-Sidecar还处于开发阶段未发布。

Sharding-JDBC表的概念

在Sharding-JDBC中,有一些表的概念,需要给大家普及一下,逻辑表、真实表、分片键、数据节点、动态表、广播表、绑定表。

逻辑表

配置文件中的定义,t_order、t_user等就是逻辑表。 后面的分库db1.t_user或者分表t_user_0等才是真实的表

spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..1}

Sharding-Sphere系列-主从配置和分库分表

广播表

广播表也叫全局表,也就是它会存在于多个库中冗余,避免跨库查询问题,比如省份、字典等一些基础数据,为了避免分库分表后关联表查询这些基础数据存在跨库问题,所以可以把这些数据同步给每一个数据库节点,这个就叫广播表

配置文件中的定义

# 广播表, 其主节点是ds0

spring.shardingsphere.sharding.broadcast-tables=t_config

spring.shardingsphere.sharding.tables.t_config.actual-data-nodes=ds$->{0}.t_config

Sharding-Sphere系列-主从配置和分库分表

绑定表

表的数据是存在逻辑的主外键关系的,比如订单表order_info,存的是汇总的商品数,商品金额;订单明细表order_detail,是每个商品的价格,个数等等。或者叫做从属关系,父表和子表的关系。他们之间会经常有关联查询的操作,如果父表的数据和子表的数据分别存储在不同的数据库,跨库关联查询也比较麻烦。所以我们能不能把父表和数据和从属于父表的数据落到一个节点上呢?比如order_id=1001的数据在node1,它所有的明细数据也放到node1;order_id=1002的数据在node2,它所有的明细数据都放到node2,这样在关联查询的时候依然是在一个数据库

Sharding-Sphere系列-主从配置和分库分表

绑定表规则,多组绑定规则使用数组形式配置

spring.shardingsphere.rules.sharding.binding-tables=t_order,t_order_item

如果存在多个绑定表规则,可以用数组的方式声明

# 绑定表规则列表

spring.shardingsphere.rules.sharding.binding-tables[0]= spring.shardingsphere.rules.sharding.binding-tables[1]= 

Sharding-JDBC中的分片策略

Sharding-JDBC内置了很多常用的分片策略,这些算法主要针对两个维度

  • 数据源分片
  • 数据表分片

Sharding-JDBC的分片策略包含了分片键和分片算法;

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

Sharding-JDBC提供内置了多种分片算法,包含四种类型分别是

  • 自动分片算法
  • 标准分片算法
  • 复合分片算法
  • Hinit分片算法

自动分片算法

自动分片算法,就是根据我们配置的算法表达式完成数据的自动分发功能,在Sharding-JDBC中提供了五种自动分片算法

  • 取模分片算法
  • 哈希取模分片算法
  • 基于分片容量的范围分片算法
  • 基于分片边界的范围分片算法
  • 自动时间段分片算法

标准分片算法

标准分片策略(StandardShardingStrategy),它只支持对单个分片健(字段)为依据的分库分表,Sharding-JDBC提供了两种算法实现

  • 行表达式分片算法

类型:INLINE

使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0 到 t_user_7

配置方法如下。

spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINEspring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.type=INLINEspring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.props.algorithm-expression=t_order_$->{order_id % 2}

  • 时间范围分片算法

和前面自动分片算法的自动时间段分片算法类似。

类型:INTERVAL

可配置属性:

属性名称

数据类型

说明

默认值

datetime-pattern

String

分片键的时间戳格式,必须遵循 Java DateTimeFormatter 的格式。例如:yyyy-MM-dd HH:mm:ss

-

datetime-lower

String

时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致

-

datetime-upper (?)

String

时间分片上界值,格式与 datetime-pattern 定义的时间戳格式一致

当前时间

sharding-suffix-pattern

String

分片数据源或真实表的后缀格式,必须遵循 Java DateTimeFormatter 的格式,必须和 datetime-interval-unit 保持一致。例如:yyyyMM

-

datetime-interval-amount (?)

int

分片键时间间隔,超过该时间间隔将进入下一分片

1

datetime-interval-unit (?)

String

分片键时间间隔单位,必须遵循 Java ChronoUnit 的枚举值。例如:MONTHS

复合分片算法

使用场景:SQL 语句中有>,>=, <=,<,=,IN 和 BETWEEN AND 等操作符,不同的是复合分片策略支持对多个分片健操作。

自定义分片算法

除了默认提供了分片算法之外,我们可以根据实际需求自定义分片算法,Sharding-JDBC同样提供了几种类型的扩展实现

  • 标准分片算法
  • 复合分片算法
  • Hinit分片策略
  • 不分片策略

分布式序列算法

Sharding-JDBC中默认提供了两种分布式序列算法

  • UUID
  • 雪花算法

可配置属性:

属性名称

数据类型

说明

默认值

worker-id (?)

long

工作机器唯一标识

0

max-vibration-offset (?)

int

最大抖动上限值,范围[0, 4096)。注:若使用此算法生成值作分片值,建议配置此属性。此算法在不同毫秒内所生成的 key 取模 2^n (2^n一般为分库或分表数) 之后结果总为 0 或 1。为防止上述分片问题,建议将此属性值配置为 (2^n)-1

1

max-tolerate-time-difference-milliseconds (?)

long

最大容忍时钟回退时间,单位:毫秒

Sharding-Sphere实战

  • 建表语句
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    -- Table structure for brand_info_0
    DROP TABLE IF EXISTS brand_info_0;
    CREATE TABLE brand_info  (
    brand_id varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '品牌ID',
    brand_name varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '品牌名称',
    telephone varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '联系电话',
    brand_web varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌网络',
    brand_logo varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌logo URL',
    brand_desc varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌描述',
    brand_status tinyint(1) NOT NULL DEFAULT 0 COMMENT '品牌状态,0禁用,1启用',
    brand_order tinyint(4) NOT NULL DEFAULT 0 COMMENT '排序',
    modified_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
    PRIMARY KEY (brand_id) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '品牌信息表' ROW_FORMAT = Dynamic;
    SET FOREIGN_KEY_CHECKS = 1;
  • 用mybatis plus generator自动生成代码。
  • 新建springcloud工程,maven依赖如下
<dependency>
      <groupId>org.apache.shardingsphere</groupId>
      <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  </dependency>

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
  </dependency>
<!--  <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
  </dependency>-->
  <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.21</version>
  </dependency>
  • 编写测试代码
/**
 * @author zhousong
 * @ClassName ShardingApplication
 * @description: 分库分表测试类
 * @datetime 2021年 11月 27日 15:05
 * @version: 1.0
 */
@SpringBootTest
public class BrandInfoServiceImplTest {
    private static final Logger logger= LoggerFactory.getLogger(BrandInfoServiceImplTest.class);
    @Resource
    public IBrandInfoService iBrandInfoService;

    @Test
    public void testSave(){
        List<BrandInfo> brandInfos=new ArrayList<BrandInfo>(12);
        BrandInfo brandInfo ;
        for (int i = 20; i < 42; i++) {
            brandInfo = new BrandInfo();
            brandInfo.setBrandDesc("toker.zhou品牌测试"+i);
            brandInfo.setBrandStatus(i%2);
            brandInfo.setBrandLogo("");
            brandInfo.setBrandName("toker.zhou品牌测试"+i);
            brandInfo.setBrandOrder(1);
            if (i<10){
                brandInfo.setTelephone("1336757129"+i);
            }else{
                brandInfo.setTelephone("133675712"+i);
            }
            brandInfo.setBrandWeb("http://minorcode.cn");
            brandInfos.add(brandInfo);
            iBrandInfoService.save(brandInfo);
        }
       logger.info("BrandInfo保存成功");
//        iBrandInfoService.saveOrUpdateBatch(brandInfos);
    }
 }
  • 首先贴出我之前的错误配置
    spring:
      shardingsphere:
        # 内存模式,元数据保存在当前进程中
        mode:
          type: Memory
        datasource:
          names: master,slave$->{0..1}
          master:
            #shardingsphere默认连接池是Hikari
            type: com.alibaba.druid.pool.DruidDataSource
            url: jdbc:mysql://192.168.118.121:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
            username: root
            password: xxxx
          slave0:
            type: com.alibaba.druid.pool.DruidDataSource   #shardingsphere默认连接池是Hikari
            url: jdbc:mysql://192.168.118.120:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
            username: root
            password: xxxx
          slave1:
            type: com.alibaba.druid.pool.DruidDataSource   #shardingsphere默认连接池是Hikari
            url: jdbc:mysql://192.168.118.122:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
            username: root
            password: xxxx
        rules:
          readwrite-splitting:
            data-sources:
              toker-source:
                load-balancer-name: round_robin_toker
                write-data-source-name: master
                read-data-source-names: slave0,slave1
            load-balancers:
              round_robin_toker:
                type: ROUND_ROBIN
          sharding:
            tables:
              brand:
                actual-data-nodes: master.brand_info_copy$->{0..1},slave$->{0..1}.brand_info_copy$->{0..1}             #也可以使用${0..n1}的形式,但是会与Spring属性文件占位符冲突,注意不要写成了$->{0,1}我之前就是在这个逗号上栽了跟头
                database-strategy:
                  standard:
                    sharding-column: brand_status
                    sharding-algorithm-name: brand_mode         
                table-strategy:
                  standard:
                    sharding-column: brand_id
                    sharding-algorithm-name: brandId_mode
                sharding-algorithms:
                  brand_mode:
                    type: MOD
                    props:
                      sharding-count: 2
                  brandId_mode:
                    type: MOD
                    props:
                      sharding-count: 2
              key-generator:
                column: brand_id
                type: SNOWFLAKE
        props:
          sql:
            show: true

    实操注意的问题:创建实体类时,默认一个实体类对应一张表,若要对应两张表,需要在properties文件中添加配置(spring.main.allow-bean-definition-overriding=true)

shardingsphere的sql日志无法打印问题

5.x版本以前
spring.shardingsphere.props.sql.show=true
5.x版本以后,sql.show参数调整为sql-show
spring.shardingsphere.props.sql-show=true
所以上面配置文件应该是
 props:
      sql-show:true

配置的雪花算法不生效

  • BrandInfo实体类auto生成的id注释
  • key-generator属于sharding的子项,而不是tables的,改正如下
// @TableId(value = "brand_id", type = IdType.AUTO)
private String brandId;

  ....................  ....................
sharding:
    tables:
      brand_info:
        ....................
          column: brand_id
          key-generator-name: Brand_SNOWFLAKE
       ....................
    key-generators:
      Brand_SNOWFLAKE:
        type: SNOWFLAKE

Field 'brand_id' doesn't have a default value

### The error may involve com.toker.cloud.platform.sharding.mapper.BrandInfoMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO brand_info  ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order )  VALUES  ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.sql.SQLException: Field 'brand_id' doesn't have a default value
; Field 'brand_id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'brand_id' doesn't have a default value
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:251)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
........................................................................................................................................................
Caused by: java.sql.SQLException: Field 'brand_id' doesn't have a default value
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:497)
at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement$2.executeSQL(ShardingSpherePreparedStatement.java:412)
at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement$2.executeSQL(ShardingSpherePreparedStatement.java:408)
at org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutorCallback.execute(JDBCExecutorCallback.java:86)
at org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutorCallback.execute(JDBCExecutorCallback.java:66)
at org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine.syncExecute(ExecutorEngine.java:135)
at org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine.parallelExecute(ExecutorEngine.java:131)

参看开头我贴出错的配置文件

sharding:
    tables:
      brand:  ##错误原因在这里
        ....................
          column: brand_id
          key-generator-name: Brand_SNOWFLAKE
       ....................
      key-generators: ####这里从原来的key-generators和tables同属于sharding的子配置项
          Brand_SNOWFLAKE:
            type: SNOWFLAKE

错误原因。我用mybatis生成的实体类brandInfo以及mapper默认的表对应的是brand_info。当shardingsphere执行 INSERT INTO brand_info ( brand_name....这条语句的时候, 由于配置文件没有brand_info这个表对应的分表配置。那么直接放过。这个时候对brand_id配置的雪花算法自然无效。所以这里有两种解决办法

解决办法:第一种办法在实体类上加上@TableName("brand"),第二种办法修改bootstrap.yml这段配置,直接修改为brand_info

sharding:
    tables:
      brand_info:     ####这里从原来的brand直接修改为brand_info
        ....................
          column: brand_id
          key-generator-name: Brand_SNOWFLAKE
       ....................
    key-generators:  ####这里从原来的key-generators和tables同属于sharding的子配置项
       Brand_SNOWFLAKE:
         type: SNOWFLAKE

Insert statement does not support sharding table routing to multiple data nodes

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: java.lang.IllegalStateException: Insert statement does not support sharding table routing to multiple data nodes.
### The error may exist in com/toker/cloud/platform/sharding/mapper/BrandInfoMapper.java (best guess)
### The error may involve com.toker.cloud.platform.sharding.mapper.BrandInfoMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO brand_info  ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order )  VALUES  ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.lang.IllegalStateException: Insert statement does not support sharding table routing to multiple data nodes.

at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
at com.sun.proxy.$Proxy216.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:271)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:60)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy217.insert(Unknown Source)

解决方案:看错误明显是insert没有路由到的问题。发现sharding-algorithms路径写到了 brand_info下面。它应该属于sharding的子项才对

sharding:
  tables:
    brand_info:
      ....................................
  sharding-algorithms:
    brand_mode:
      type: MOD
      props:
        sharding-count: 2
    brandId_mode:
      type: MOD
      props:
        sharding-count: 2

No database route info

### SQL: INSERT INTO brand_info  ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order )  VALUES  ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.lang.IllegalStateException: No database route info

at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
at com.sun.proxy.$Proxy216.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:271)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:60)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy217.insert(Unknown Source)

正确配置要点

  • 主从配置数据源(主库写、从库读)。即readwrite_ds(这个是在spring.shardingsphere.rules.readwrite-splitting.data-sources下自定义的名称)
  • 分库分表的的数据源要从前面的主从配置数据源获取即readwrite_ds ,actual-data-nodes: readwrite_ds.brand_info_$->{0..1},这里的$->{0..1}是为了防止和spring的配置文件占位符{0..1}起冲突.
  • 切勿网上拷贝的配置,从github找到相应的版本的example核对下配置.比如我是5.1.2的版本。则核对位置;https://github.com/apache/shardingsphere/blob/5.1.2/examples/shardingsphere-jdbc-example/mixed-feature-example/sharding-readwrite-splitting-example/sharding-readwrite-splitting-spring-boot-mybatis-example/src/main/resources/application-sharding-readwrite-splitting.properties

主从分离和分库分表配置(修正后)

spring:
  shardingsphere:
    # 内存模式,元数据保存在当前进程中
    mode:
      type: Memory
    datasource:
      names: master,slave0,slave1
      master:
        #shardingsphere默认连接池是Hikari
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://192.168.118.121:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: xxxx
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource   #shardingsphere默认连接池是Hikari
        url: jdbc:mysql://192.168.118.120:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: xxxx
      slave1:
        type: com.alibaba.druid.pool.DruidDataSource   #shardingsphere默认连接池是Hikari
        url: jdbc:mysql://192.168.118.122:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: xxxx
    rules:
      readwrite-splitting:
        data-sources:
          readwrite_ds:
            type: Static
            props:
              write-data-source-name: master
              read-data-source-names: slave0,slave1
            load-balancer-name: round_robin_toker
        load-balancers:
          round_robin_toker:
            type: ROUND_ROBIN
      sharding:
        tables:
          brand_info:
            actual-data-nodes: readwrite_ds.brand_info_$->{0..1}
#           database-strategy:
#             standard:
#               sharding-column: brand_status
#               sharding-algorithm-name: brand_mode
            table-strategy:
              standard:
                sharding-column: brand_status
                sharding-algorithm-name: brand_mode
            key-generate-strategy:
              column: brand_id
              key-generator-name: Brand_SNOWFLAKE
        sharding-algorithms:
          brand_mode:
            type: MOD
            props:
              sharding-count: 2
#         brandId_mode:
#           type: MOD
#           props:
#             sharding-count: 2
        key-generators:
          Brand_SNOWFLAKE:
            type: SNOWFLAKE
    props:
#     sql:
#       show: true
      sql-show: true
    enabled: true
  main:
    allow-bean-definition-overriding: true

QueryWrapper和LambdaQueryWrapper

在前面的BrandInfoServiceImplTest中再添加一个查询类,结合前面的写入类观测结果

 @Test
    public void  findByBrandStatus(){

        QueryWrapper<BrandInfo> queryWrapper = new QueryWrapper<BrandInfo>();
// 面向表字段的查询
        queryWrapper.eq("brand_status",0);
        Page<BrandInfo> page=new Page<BrandInfo>(1,2);
        IPage<BrandInfo> results=iBrandInfoService.page(page,queryWrapper);
        logger.info("case1查询出的分页对象为:{}", JSON.toJSON(results));

        Page<BrandInfo> page2=new Page<BrandInfo>(2,2);

        //面向对象的写法
        LambdaQueryWrapper<BrandInfo> lambdaQueryWrapper = new LambdaQueryWrapper<BrandInfo>();

        lambdaQueryWrapper.eq(BrandInfo::getBrandStatus,1);
    //下面这段方法在mybatis在做属性转换时候值是一段函数。而非从get或者is解析出来的值
//        lambdaQueryWrapper.eq((x)->{
//            return x.getBrandStatus();
//        },1);
        IPage<BrandInfo> results2=iBrandInfoService.page(page2,lambdaQueryWrapper);
        logger.info("case2查询出的分页对象为:{}", JSON.toJSON(results2));

//        queryWrapper.clear();
//        queryWrapper.eq("brand_status",1);
//        IPage<BrandInfo> results3=iBrandInfoService.page(page,lambdaQueryWrapper);
//        logger.info("case3查询出的分页对象为:{}", JSON.toJSON(results3));

    }

执行testSave()和findByBrandStatus()测试

  1. 先执行BrandInfoServiceImplTest的testSave()方法,观测写库结果,可以看到status为1的和为0的在不同的表里。分表成功。Sharding-Sphere系列-主从配置和分库分表Sharding-Sphere系列-主从配置和分库分表
  2. 执行findByBrandStatus()的方法。观测实际生成的sql语句和打印的分页的执行结果,可以看到一个是查询的slave0的brand_info_0 ,一个查询的是slave1的brand_info_1表
    Actual SQL: slave0 ::: SELECT  brand_id,brand_name,telephone,brand_web,brand_logo,brand_desc,brand_status,brand_order,modified_time  FROM brand_info_0  WHERE (brand_status = ?) LIMIT ? ::: [0, 2]
    2022-11-29 15:21:35.003  INFO 56624 --- [           main] c.t.c.p.s.s.i.BrandInfoServiceImplTest   : case1查询出的分页对象为:{"current":1,"total":17,"hitCount":false,"pages":9,"optimizeCountSql":true,"size":2,"records":[{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:10","brandName":"toker.zhou品牌测试0","brandDesc":"toker.zhou品牌测试0","brandId":"804289715246202880","telephone":"13367571290","brandStatus":0,"brandLogo":"","brandOrder":1},{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:12","brandName":"toker.zhou品牌测试2","brandDesc":"toker.zhou品牌测试2","brandId":"804292230620643328","telephone":"13367571292","brandStatus":0,"brandLogo":"","brandOrder":1}],"searchCount":true,"orders":[]}
    
    : Actual SQL: slave1 ::: SELECT  brand_id,brand_name,telephone,brand_web,brand_logo,brand_desc,brand_status,brand_order,modified_time  FROM brand_info_1  WHERE (brand_status = ?) LIMIT ?,? ::: [1, 2, 2]
    2022-11-29 15:21:35.066  INFO 56624 --- [           main] c.t.c.p.s.s.i.BrandInfoServiceImplTest   : case2查询出的分页对象为:{"current":2,"total":17,"hitCount":false,"pages":9,"optimizeCountSql":true,"size":2,"records":[{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:17","brandName":"toker.zhou品牌测试5","brandDesc":"toker.zhou品牌测试5","brandId":"804292251910930433","telephone":"13367571295","brandStatus":1,"brandLogo":"","brandOrder":1},{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:19","brandName":"toker.zhou品牌测试7","brandDesc":"toker.zhou品牌测试7","brandId":"804292259913662465","telephone":"13367571297","brandStatus":1,"brandLogo":"","brandOrder":1}],"searchCount":true,"orders":[]}

    至此主从+分库分表配置测试成功。 下一步计划将跟踪Debug的源码分析贴出来。

一个小插曲

错误写法:lambdaQueryWrapper.eq((x)->{
            return x.getBrandStatus();
        },1);
正确写法:lambdaQueryWrapper.eq(BrandInfo::getBrandStatus,1);

 如果用lambda表达式,这个name的值会被判定是一段函数,mybatis在做属性转换的时候直接报错

Sharding-Sphere系列-主从配置和分库分表文章来源地址https://www.toymoban.com/news/detail-402754.html

到了这里,关于Sharding-Sphere系列-主从配置和分库分表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Sharding-JDBC分库分表四种分片算法

    精确分片算法(PreciseShardingAlgorithm)精确分片算法(=与IN语句),用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用 范围分片算法(RangeShardingAlgorithm)用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingS

    2024年02月10日
    浏览(38)
  • springboot整合sharding-jdbc实现分库分表详解

    目录 一、为什么需要分库分表 1.1 分库分表的优势 二、分库分表基本概念 2.1 垂直分表

    2024年02月05日
    浏览(54)
  • Sharding JDBC 分库分表(一致性Hash + 虚拟节点)

    一、背景 传统的将数据集中存储至单一数据节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足互联网的海量数据场景。 从 性能 方面来说,由于关系型数据库大多采用B+树类型的索引,在数据量超过阈值的情况下,索引深度的增加也将使得磁盘访问的IO次数

    2024年02月10日
    浏览(43)
  • Mybatis-Plus集成Sharding-JDBC与Flyway实现多租户分库分表

    公司产品部收到了一些重要客户的需求,他们希望能够依赖独立的数据库存储来支持他们的业务数据。与此同时,仍有许多中小客户,可以继续使用公共库以满足其需求。技术实现方面,此前持久层框架使用的Mybatis-plus,部分业务场景使用到了Sharding-JDBC用于分表,另外,我们

    2024年02月05日
    浏览(34)
  • Flink CDC获取mysql 主从分库,分库分表的binlog

    Flink CDC可以获取MySQL主从分库,分库分表的binlog,但是需要注意以下几点: Flink CDC需要配置MySQL的binlog模式为row,以及开启GTID(全局事务标识符),以便正确地识别和处理binlog事件 Flink CDC需要配置MySQL的主从复制关系,以及指定主库或从库的地址,以便正确地连接和读取bin

    2024年02月11日
    浏览(41)
  • 分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离(二)

            说明:如果实现了docker部署mysql并完成主从复制的话再继续,本篇文章主要说明springboot配置实现Shardingjdbc进行读写分离操作。 如果没实现docker部署mysql实现主从架构的话点击我 application.yml配置设置连接池全局属性 shardingjdbc读写分离配置(qiyu-db-sharding.yaml) 同时这

    2024年02月15日
    浏览(40)
  • 分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离 (三)

            本篇主要说明:                 1. 因为这个mysql版本是8.0,所以当其中一台mysql节点挂掉之后,主从同步,甚至双向数据同步都失效了,所以本篇主要记录下当其中的节点挂掉之后如何再次生效。 另外推荐大家使用mysql5.7的版本,这样当其他节点失效后就不

    2024年02月14日
    浏览(37)
  • [SQL系列] 从头开始学PostgreSQL 分库分表

            分库分表是一种数据库架构设计的方法,用于应对大规模数据的存储和查询。当单个数据库的存储容量或查询性能无法满足需求时,可以通过将数据分散存储在多个数据库服务器上,以提高系统的可扩展性和性能。         分库分表通常包括两个步骤:分库和

    2024年02月14日
    浏览(36)
  • ​​​​​​C#系列-C#EF框架实现分库分表(21)

    在 C# 中使用 Entity Framework (EF) 框架实现分库分表(也称为数据库分片或水平切分)是一个相对复杂的过程,因为 EF 本身并不直接支持分库分表。分库分表通常是为了解决单一数据库的性能瓶颈、数据量过大、高并发等问题而采取的一种策略。 实现分库分表通常涉及以下几个

    2024年02月20日
    浏览(35)
  • shardingsphere5.1.1分表分库yaml配置 自定义策略

    通过阅读官方稳定给出示例 https://shardingsphere.apache.org/document 在该配置中,有两个数据源ds0和ds1,分别对应两个数据库db0和db1。在ShardingSphere中,通过配置actual-data-nodes属性来指定数据分片的具体情况。在这里,我们指定了user表在ds0和ds1这两个数据源中的分片情况。其中,ac

    2024年02月09日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包