一次sharding-jdbc 5.0 踩坑历程

这天霞妹又来找烧哥了。

“烧哥,帮我看下这个分库分表,升级5.0后不能用了呢?”
“Let me c c?哇,Sharding-jdbc刚推出的最新版?”

背景

关系数据库当今依然占有巨大市场份额,前期通常会存储至单一节点。为提高性能和可用性,一种方案是迁移到NoSQL ,但会有较大技术成本。另一种则考虑数据分片,按照某个维度将数据分散地存储到多个库或表,来有效的避免大数据量产生的查询瓶颈。

数据分片也有两种。垂直分片讲究专库专用,通常按业务模块划分。水平分片则是按字段,通过某种规则拆分到不同库或表。通过搭建多主多从的数据库架构,读写分离,配合水平拆分,实际场景中较为常见。

ShardingSphere则同时提供了这两种解决方案,2020.4.16成为 Apache 软件基金会的顶级项目。Sharding-jdbc作为子产品,以Jar包形式提供服务,可理解为增强版的 JDBC 驱动,能够几乎不改动代码的情况下实现架构迁移,2021.11.10推出了5.0.0版。
一次sharding-jdbc 5.0 踩坑历程_第1张图片

问题重现

首先看之前的配置文件:
pom.xml


    org.apache.shardingsphere
    sharding-jdbc-spring-boot-starter
    4.1.1

application.yml

spring:
  shardingsphere:
    datasource:
      names: master1,slave1
      master1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        password: 
        type: com.alibaba.druid.pool.DruidDataSource
        url: 
        username: 
      slave1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        password: 
        type: com.alibaba.druid.pool.DruidDataSource
        url: 
        username: 
    props:
      sql:
        show: true
    sharding:
      tables:
        b_gcg_content:
          actual-data-nodes: ds.b_gcg_content_$->{0..1}
          table-strategy:
            inline:
              sharding-column: content_id
              algorithm-expression: b_gcg_content_$->{content_id % 2}
              key-generator:
                column: content_id
                type: SNOWFLAKE
      master-slave-rules:
        ds:
          load-balance-algorithm-type: round_robin
          master-data-source-name: master1
          slave-data-source-names: slave1

主库master1,从库slave1,对content表根据content_id取模拆分为两个表。

查询,插入,运行正常。

再看之后的:
pom.yml


    org.apache.shardingsphere
    shardingsphere-jdbc-core-spring-boot-starter
    5.0.0

application.yml

spring:
  shardingsphere:
    datasource:
      names: master1,slave1
      master1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: 
        username: 
        password: 
      slave1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: 
        username: 
        password: 
    props:
      sql-show: true
    rules:
      sharding:
        tables: # 数据分片规则配置
          b_gcg_content: # 逻辑表名称
            actualDataNodes: ds.b_gcg_content_$->{0..1} # 由数据源名 + 表名组成(参考Inline语法规则)
            tableStrategy: # 分表策略,同分库策略
              standard: # 用于单分片键的标准分片场景
                shardingColumn: content_id # 分片列名称
                shardingAlgorithmName: my # 分片算法名称
            keyGenerateStrategy: # 分布式序列策略
              column: content_id # 自增列名称,缺省表示不使用自增主键生成器
              keyGeneratorName: my # 分布式序列算法名称
        shardingAlgorithms:
          my: # 分片算法名称
            type: INLINE  # 分片算法类型
            props: # 分片算法属性配置
              algorithm-expression: b_gcg_content_$->{content_id % 2}
        keyGenerators:
          my: # 分布式序列算法名称
            type: SNOWFLAKE # 分布式序列算法类型
      readwriteSplitting:
        dataSources:
          ds:
           loadBalancerName: my
           writeDataSourceName: master1
           readDataSourceNames: slave1
        loadBalancers:
          my: # 负载均衡算法名称
             type: ROUND_ROBIN # 负载均衡算法类型

查询正常,插入时报错:

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

解谜

1.是否符合官方标准?(不熟悉的话常犯)

首先看到配置文件的语法,升级后有很大改变,根据官方文档挨个排查,确认格式全部正确。

2.是否新版本有缺陷?(最近经常有新闻,xx又有重大漏洞)

搜索这个报错,全网都没有。到官方仓库issue,有大把。引起的原因有很多,但都已修复。
那就下最新源码,5.0.1-SNAPSHOT,重新编译放入私服,岂不简单?
编译比较麻烦,不过最终还是成功了,更改maven版本,奇怪了,依然报错。

3.化繁为简,缩小范围,精准定位(首要思路)

报错中提到了data nodes,配置里同时有读写分离和分表,那就去掉一个,只要分表

    rules:
      sharding:
        tables: # 数据分片规则配置
          b_gcg_content: # 逻辑表名称
            actualDataNodes: master1.b_gcg_content_$->{0..1} # 由数据源名 + 表名组成(参考Inline语法规则)
            tableStrategy: # 分表策略,同分库策略
              standard: # 用于单分片键的标准分片场景
                shardingColumn: content_id # 分片列名称
                shardingAlgorithmName: my # 分片算法名称
            keyGenerateStrategy: # 分布式序列策略
              column: content_id # 自增列名称,缺省表示不使用自增主键生成器
              keyGeneratorName: my # 分布式序列算法名称
        shardingAlgorithms:
          my: # 分片算法名称
            type: INLINE  # 分片算法类型
            props: # 分片算法属性配置
              algorithm-expression: b_gcg_content_$->{content_id % 2}
        keyGenerators:
          my: # 分布式序列算法名称
            type: SNOWFLAKE # 分布式序列算法类型

还是报错。
那不分表总可以吧?再来

    rules:
      sharding:
        tables: # 数据分片规则配置
          b_gcg_content: # 逻辑表名称
            actualDataNodes: master1.b_gcg_content # 由数据源名 + 表名组成(参考Inline语法规则)
            tableStrategy: # 分表策略,同分库策略
              standard: # 用于单分片键的标准分片场景
                shardingColumn: content_id # 分片列名称
                shardingAlgorithmName: my # 分片算法名称
            keyGenerateStrategy: # 分布式序列策略
              column: content_id # 自增列名称,缺省表示不使用自增主键生成器
              keyGeneratorName: my # 分布式序列算法名称
        shardingAlgorithms:
          my: # 分片算法名称
            type: INLINE  # 分片算法类型
            props: # 分片算法属性配置
              algorithm-expression: b_gcg_content
        keyGenerators:
          my: # 分布式序列算法名称
            type: SNOWFLAKE # 分布式序列算法类型

这下倒是可以,看来的确是分表有问题。

4.跟正常的对比,由外到内,等价替换(廉价快速手段)

那官方demo不会也报错吗?apache出品的不至于吧?怀疑人生。
4.x的shardingsphere-example项目已经停更,新demo合并到了主库。
但官方demo是正常的?那就蹊跷了。
难道我的表不行?换成demo的库试试,还是报错,奇葩。
再来看看上面配置,唯一的区别就在于名称改成了my,难不成名称是不能改的??
改成跟demo一样名称,竟然行了。。。

5.避坑
spring:
  shardingsphere:
    datasource:
      names: master1,slave1
      master1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: 
        username: 
        password: 
      slave1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: 
        username: 
        password: 
    props:
      sql-show: true
    rules:
      sharding:
        tables: # 数据分片规则配置
          b_gcg_content: # 逻辑表名称
            actualDataNodes: ds.b_gcg_content_$->{0..1} # 由数据源名 + 表名组成(参考Inline语法规则)
            tableStrategy: # 分表策略,同分库策略
              standard: # 用于单分片键的标准分片场景
                shardingColumn: content_id # 分片列名称
                shardingAlgorithmName: my-table # 分片算法名称
            keyGenerateStrategy: # 分布式序列策略
              column: content_id # 自增列名称,缺省表示不使用自增主键生成器
              keyGeneratorName: my-key # 分布式序列算法名称
        shardingAlgorithms:
          my-table: # 分片算法名称
            type: INLINE  # 分片算法类型
            props: # 分片算法属性配置
              algorithm-expression: b_gcg_content_$->{content_id % 2}
        keyGenerators:
          my-key: # 分布式序列算法名称
            type: SNOWFLAKE # 分布式序列算法类型
      readwriteSplitting:
        dataSources:
          ds:
           loadBalancerName: my-load
           writeDataSourceName: master1
           readDataSourceNames: slave1
        loadBalancers:
          my-load: # 负载均衡算法名称
             type: ROUND_ROBIN # 负载均衡算法类型

“霞妹可以啦”
“烧哥好棒,我这有张券,中午一起嘛”

填坑

架构师的工作就是解决各种疑难杂症,思路的锻炼来自长期实战经历。

Apache的品质总体还是可信赖的,这算个小问题。源码应该是把某些名称放在了一个map下,或者是缓存时出了岔子,有空去提个issue.

又增加一条规范:复杂配置文件中,自定义名称不应该重复。

你可能感兴趣的:(架构成长,数据库,Sharding-jdbc,架构,sharding,数据库)