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

news2024/10/2 6:27:43

主从配置和分库分表

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}

广播表

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

配置文件中的定义

# 广播表, 其主节点是ds0

spring.shardingsphere.sharding.broadcast-tables=t_config

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

绑定表

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

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

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的在不同的表里。分表成功。
  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在做属性转换的时候直接报错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/544693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mybatis-Plus 自动属性填充与自定义Insert into语句顺序

前言&#xff1a;系统中使用了Mybatis-Plus 自动属性填充为实体统一进行属性的填值&#xff0c;在Mapper的xml 文件中 insert into 语句 使用 <if test"id ! null">id,</if> 进行判断会发现该属性是空的&#xff0c;明明已经为改字段进行了属性的自动填充…

百度2023年Q1财报解析:AI+生态战略加速助推

原创 | 文 BFT机器人 01 百度靠AI实现翻身 &#xff08;一&#xff09;盈利能力 百度凭借着强大的AI能力&#xff0c;成功地实现了从依赖搜索业务的互联网公司到AI公司的转型。 从盈利能力层面上看&#xff0c;在第一季度&#xff0c;百度实现了营收311.44亿元&#xff0c;同比…

unity愤怒的小鸟学习制作(二)

终于又开始了啦啦啦&#xff0c;我有一个自己的相机了&#xff0c;真开心&#xff0c;诶嘿 视频链接和素材如下&#xff1a;视频 小鸟的飞出 想要让小鸟在拉开弹弓之后能飞出去&#xff0c;就必须让这个组件失活&#xff0c;如下 所以我们更改脚本内容&#xff0c;加入&#…

HarmonyOS应用端云一体化开发主要流程

图示 主要步骤 序号 阶段 任务 说明 1 创建端云一体化开发工程 选择工程类型与云开发模板 确定工程类型&#xff1a;选择“Application”或“Atomic Service”页签&#xff0c;确定创建的是HarmonyOS应用工程还是原子化服务工程。选择云开发模板&#xff0c;包括通用云开…

【FMC202】基于FMC标准的1路CameraLink Full 输入、1路DVI输出 子卡模块

产品概述 FMC202是一款基于FMC接口标准的1路CameraLink Full模式&#xff08;或者2路CameraLink Base模式&#xff09;采集、1路HDMI&#xff08;DVI&#xff09;视频输出的子卡模块&#xff0c;该模块具有2个CameraLink端口&#xff08;SDR&#xff0c;26PIN&#xff09;&…

Docker部署apache superset

使用Docker compose在docker中部署Apache Superset 说明&#xff1a;部署步骤按照superset官网说明总结而来-2023年 1、第一步安装docker 、docker compose。 这里我选择手动下载rpm包&#xff0c;然后yum install *.rpm方式来安装。 下载地址&#xff1a;https://download.…

MADDPG-学习笔记(2)

注意&#xff1a;进行本文的实验前&#xff0c;为了加快训练速度&#xff0c;进行了参数调整 num-episodes&#xff1a;由60000改成了10000 lr:由0.01改成了0.1 batch-size:由1024改成了32 1.报错 1.1 AttributeError: Scenario object has no attribute benchmark_data …

ApiKit 介绍及基本用法

1、ApiKit介绍及下载 ApiKitAPI 管理 Mock 自动化测试 异常监控 团队协作 结合 API 设计、文档管理、自动化测试、监控、研发管理和团队协作的一站式 API 生产平台&#xff0c;从个人开发者到跨国企业用户&#xff0c;Apikit 帮助全球超过50万开发者和10万家企业更快、更好…

【Flutter】Flutter CLI (2):调试分析项目 flutter analyze 命令详解

文章目录 一、前言二、对现有项目进行分析和调试1. flutter analyze2. 修改代码暴露错误3. 再次执行flutter analyze4. 调试模式运行代码 flutter run三、本文涉及命令的完整说明1.flutter analyze四、总结一、前言 在上一篇文章中,我们对 Flutter CLI 的命令进行了分类,并通…

Array的扩展方法(from、find、findlndex、includes)

Array.from() 构造函数方法:Array.from() 将类数组或可遍历对象转换为真正的数组 示例 <script>// 构造函数方法:Array.from()// 将类数组或可遍历对象转换为真正的数组let arr {0: a,1: b,2: c,length: 3,};let arr1 Array.from(arr);console.log(arr1);//["…

对回溯的理解与思考(从决策树遍历角度分析)

对于回溯的经典问题&#xff0c;就是全排列和各种各样全排列的变体和八皇后问题。 算法框架 对于回溯算法框架。其实解决一个回溯问题&#xff0c;实际上就是一个决策树的遍历过程。 这也就是为什么在刷算法题之前&#xff0c;一定要从树的题目开始刷&#xff0c;后期可以很方…

检索 COM 类工厂中 CLSID 为 {} 的组件失败, 内存资源不足,无法处理此命令

如果您收到ERROR_NOT_ENOUGH_MEMORY消息&#xff0c;提示没有足够的存储空间来处理此命令描述&#xff0c;请按照本文中列出的故障排除步骤进行修复。 此错误代码影响Windows服务器&#xff0c;导致系统崩溃&#xff0c;并在错误日志中显示“没有足够的存储空间来处理此命令”。…

Qt+QtWebApp开发笔记(二):http服务器日志系统介绍、添加日志系统至Demo测试

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/130762721 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

Clickhouse 入门到精通-Clickhouse工作原理

Clickhouse 为什么做查询分析那么快&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 因为clickhouse使用了下列方案&#xff1a; clickhouse 数据分区clickhouse 列式存储clickhouse 一级索引&#xff08;主键索引&#…

企业数字转型加速器!居然是他!该不会还有人没用上吧?

随着数字化时代的到来和技术的发展&#xff0c;企业数字化转型已经成为全球企业发展的重要趋势。然而&#xff0c;数字化转型的过程却并非一帆风顺&#xff0c;常常因为 IT 复杂度高、开发周期长等问题而遇到许多挑战&#xff0c;这时候低代码开发平台就能够发挥重要作用。 低代…

我们为什么还要学习Altium Designer?

Altium Designe&#xff08;简称“AD”&#xff09;是电子设计领域中备受推崇的软件工具之一&#xff0c;拥有强大的功能和灵活的设计环境&#xff0c;也是要用最广泛的EDA工具之一&#xff0c;为电子工程师提供了无限可能&#xff0c;但很多工程师学完AD基本操作就转投其他EDA…

支付宝小程序打包成APP

发行——原生App-云打包——填写安卓包的信息&#xff08;安卓证书可在香蕉云编下载&#xff09;——打包——下载APK 第一步&#xff1a;点击菜单栏发行 第二步&#xff1a;选择远程APP-云打包 第三步&#xff1a;在香蕉云编&#xff08;https://www.yunedit.com/&#xff0…

K8S之yaml文件,声明式管理方法

目录 第一章.声明式管理方法 1.1.声明式管理方法 1.2.kubectl create 和 kubectl apply区别 1.3.查看资源配置清单 1.4.解释资源配置清单 1.5.修改资源配置清单并应用 第二章.yaml文件格式 2.1.yaml文件简述 2.2.YAML 语法格式 2.3.查看 api 资源版本标签 2.4.写一个…

MVC中Controller向View传值的几种方式

MVC中Controller向View传值的几种方式 文章目录 MVC中Controller向View传值的几种方式一、ViewModel使用ViewModel 二、ViewData在控制器和视图间使用ViewData传递数据在 ViewDataTest 视图中使用ViewData的数据在视图和部分视图间使用ViewData 三、ViewBag四、TempData五、Ses…

搭建短链服务

目录 一、背景 1.1短链接的优势 1.1.1优点一 1.1.2优点二 1.1.3优点三 1.1.4优点四 1.1.4优点五 二、原理 2.1利用http重定向 3.1实现方案 3.1.1发号器实现 3.1.2存储实现 3.1.3映射实现 3.2架构图 一、背景 短链在互联网中盛行&#xff0c;搭建自己短链平台&…