乐趣区

关于java:分库分表如何管理不同实例中几万张分片表

大家好,我是小富~

ShardingSphere 实现分库分表,如何治理散布在不同数据库实例中的成千上万张分片表?

上边的问题是之前有个小伙伴看了我的分库分表的文章,私下征询我的,看到他的发问我第一感觉就是这老铁没用过 ShardingSphere,因为这个问题在 ShardingSphere 中曾经有了很好的解决方案,接下来看看怎么实现。

本文案例代码 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-autocreate-table

系列往期

往期系列文章(我佛系更新,无上限拖更):

(一)好好的零碎,为什么要分库分表?

(二)分库分表的 21 条法令,hold 住!

(三)2 种形式疾速实现分库分表,轻松拿捏!

本文是《ShardingSphere5.x 分库分表原理与实战》系列的第四篇文章,在进行分库分表设计时,确认好了数据节点数量和分片策略当前,接下来要做的就是治理大量的分片表。理论施行过程中可能存在上百个分片数据库实例,每个实例中都可能有成千上万个分片表,如果仅依附人力来实现这些工作显然是不事实的。所以,想要疾速且自动化治理这些分片表,应用工具是十分必要滴。

前言

ShardingSphere框架成员中的 Shardingsphere-jdbcShardingsphere-proxy都提供了自动化治理分片表的性能auto-tables,能够对立保护大量的分片表,防止了手动编写脚本和保护分片表的繁琐工作,极大水平缩小分库分表的开发和保护老本,晋升效率和可靠性。

这里咱们先应用 Shardingsphere-jdbc 来实际操作一下,Shardingsphere-proxy形式后续会有独自的文章具体解说,就不在这里开展了。

筹备工作

假如咱们要对 t_order 表进行分库分表,首先咱们要做的就是确定好分片计划,这里应用两个数据库实例 db0db1,每个实例中t_order 表分成 1000 张分片表 t_order_1 ~ t_order_1000order_id 字段作为分片键,分片算法应用取模算法order_id % n,分布式主键生成策略采纳snowflake

t_order逻辑表的表构造如下:

CREATE TABLE `t_order` (`order_id` BIGINT ( 20) NOT NULL COMMENT "订单表分布式主健 ID",
    `order_number` VARCHAR (255) NOT NULL COMMENT "订单号",
    `customer_id` BIGINT (20) NOT NULL COMMENT "用户 ID",
    `order_date` date NOT NULL COMMENT "下单工夫",
    `total_amount` DECIMAL (10, 2) NOT NULL COMMENT "订单金额",
    PRIMARY KEY (`order_id`) USING BTREE 
);

有了这些根底信息,能够先来进行 t_order 表的分片配置了,不思考其余因素,这里先 Run 起来!

分片规定配置

设定好分片规定,接着编写逻辑表 t_order 的分片规定的配置,我别离应用 yml 配置Java 编码 两种形式做了实现。要留神的是两种形式不要并存,不然启动会报错

yml 配置形式

应用 yml 配置绝对简略易用比拟直观,适宜对分库分表要求不太简单的场景,残缺配置如下:

spring:
  shardingsphere:
    datasource:
      # 数据源名称,多数据源以逗号分隔 , 放在第一个的数据源为未配置分片规定表的默认数据源
      names: db0 , db1
      # 名称与上边 names 保持一致
      db0:
      ....

      db1:
      ....
    # 具体规定配置
    rules:
      sharding:
        # 分片算法定义
        sharding-algorithms:
          # 自定义分片算法名称
          t_order_database_algorithms:
            # 分片算法类型
            type: INLINE
            # 自定义参数
            props:
              algorithm-expression: db$->{order_id % 2}
          t_order_table_algorithms:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{order_id % 1000}
          t_order_mod:
            type: MOD
            props:
              # 指定分片数量
              sharding-count: 1000
        # 分布式序列算法配置
        key-generators:
          t_order_snowflake:
            type: SNOWFLAKE
            # 分布式序列算法属性配置
            props:
              worker-id: 1
        tables:
          # 逻辑表名称
          t_order:
            # 数据节点:数据库. 分片表
            actual-data-nodes: db$->{0..1}.t_order_$->{1..1000}
            # 分库策略
            database-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: t_order_database_algorithms
            # 分表策略
            table-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: t_order_table_algorithms
            # 主键生成策略
            keyGenerateStrategy:
              column: order_id
              keyGeneratorName: t_order_snowflake
    # 属性配置
    props:
      # 展现批改当前的 sql 语句
      sql-show: true

Java 编码方式

应用 Java 编码方式更加灵便和可扩大,能够依据业务定制分片规定,适宜对分库分表有非凡需要或须要动静调整的场景。

/**
 * 公众号:程序员小富
 */
@Configuration
public class ShardingConfiguration {

    /**
     * 配置分片数据源
     * 公众号:程序员小富
     */
    @Bean
    public DataSource getShardingDataSource() throws SQLException {Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("db0", dataSource0());
        dataSourceMap.put("db1", dataSource1());

        // 分片 rules 规定配置
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        // 分片算法
        shardingRuleConfig.setShardingAlgorithms(getShardingAlgorithms());
        // 配置 t_order 表分片规定
        ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_order", "db${0..1}.t_order_${1..1000}");
        orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "t_order_table_algorithms"));
        orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "t_order_database_algorithms"));
        shardingRuleConfig.getTables().add(orderTableRuleConfig);

        // 是否在控制台输入解析革新后实在执行的 SQL
        Properties properties = new Properties();
        properties.setProperty("sql-show", "true");

        // 创立 ShardingSphere 数据源
        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), properties);
    }

    /**
     * 配置数据源 1
     * 公众号:程序员小富
     */
    public DataSource dataSource0() {HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    /**
     * 配置数据源 2
     * 公众号:程序员小富
     */
    public DataSource dataSource1() {HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    /**
     * 配置分片算法
     * 公众号:程序员小富
     */
    private Map<String, AlgorithmConfiguration> getShardingAlgorithms() {Map<String, AlgorithmConfiguration> shardingAlgorithms = new LinkedHashMap<>();

        // 自定义分库算法
        Properties databaseAlgorithms = new Properties();
        databaseAlgorithms.setProperty("algorithm-expression", "db$->{order_id % 2}");
        shardingAlgorithms.put("t_order_database_algorithms", new AlgorithmConfiguration("INLINE", databaseAlgorithms));

        // 自定义分表算法
        Properties tableAlgorithms = new Properties();
        tableAlgorithms.setProperty("algorithm-expression", "db$->{order_id % 1000}");
        shardingAlgorithms.put("t_order_table_algorithms", new AlgorithmConfiguration("INLINE", tableAlgorithms));

        return shardingAlgorithms;
    }
}

下面咱们在利用中编写好了分片规定,当初就差在数据库实例中创立分片表了,手动创立和治理 1000 张分片表的确是一个又脏又累的活,反正我是不会干的!

治理分片表

其实,ShardingSphere内曾经为咱们提供了治理分片表的能力。

当一张逻辑表 t_order 被配置了分片规定,那么接下来对逻辑表的各种 DDL 操作(例如 创立表 批改表构造 等),命令和数据会依据分片规定,执行和存储到每个分片数据库和分片库中的相应分片表中,以此放弃整个分片环境的一致性。

不过,应用 Shardingsphere-jdbc 治理分片表的过程中,是须要咱们手动编写对逻辑表的 DDL 操作的代码。咱们来跑几个单元测试用例来察看理论的执行成果,间接应用 jdbcTemplate 执行创立逻辑表 t_order 的 SQL。

/**
 * @author 公众号:程序员小富
 * 主动创立分片表
 * @date 2023/12/31 17:25
 */
@SpringBootTest
class AutoCreateTablesTests {
    @Resource
    private JdbcTemplate jdbcTemplate;
    /**
     * 执行创立逻辑表的 SQL,会依据 AutoTables 的配置主动在对应的数据源内创立分片表
     * @author 公众号:程序员小富
     */
    @Test
    public void autoCreateOrderTableTest() {

        jdbcTemplate.execute("CREATE TABLE `t_order` (\n" +
                "`order_id` bigint(20) NOT NULL,\n" +
                "`order_number` varchar(255) NOT NULL,\n" +
                "`customer_id` bigint(20) NOT NULL,\n" +
                "`order_date` date NOT NULL,\n" +
                "`total_amount` decimal(10,2) NOT NULL,\n" +
                "PRIMARY KEY (`order_id`) USING BTREE\n" +
                ");");
    }
}

依据之前配置的分片规定,将会在两个数据库实例 db0db1 中,别离生成 1000 张命名为 t_order_1t_order_1000的分片表,看到两个数据库均胜利创立了 1000 张分片表。

在次执行更新 t_order 表 SQL,将字段 order_number 长度从 varchar(255)扩大到 varchar(500),执行 SQL 看下成果。

/**
 * @author 公众号:程序员小富
 * 主动创立分片表
 * @date 2023/12/31 17:25
 */
@SpringBootTest
class AutoCreateTablesTests {
    @Resource
    private JdbcTemplate jdbcTemplate;
    
    @Test
    public void autoModifyOrderTableTest() {jdbcTemplate.execute("ALTER TABLE t_order MODIFY COLUMN order_number varchar(500);");
    }
}

通过查看两个分片库,咱们胜利地将所有分片表的 order_number 字段长度更改为了varchar(500),在控制台日志中,能够看到它是通过在每个分片库内顺次执行了 1000 次命令实现的。

Shardingsphere-jdbc实现分库分表时,能够采纳这种默认的形式来治理分片表。但要留神的是,因为波及到不同的数据库实例,如果不应用第三方的分布式事务管理工具(例如 Seata 等),执行过程是无奈保障事务一致性的。

自定义治理分片表

上边为逻辑表配置分片规定,应用程序内执行对逻辑表的 DDL 操作,就能够很轻松的治理分片表。

自定义

不过,默认的分片治理还是有局限性的,咱们在设计分片规定时往往会依据不同的业务维度来划分,例如按天、月、按季度生成分片表并散布到不同数据源中等。这样就须要一些自定义的规定来实现。

ShardingSphere 5.X版本后推出了一种新的治理分片配置形式:AutoTable。设置了 AutoTable 的逻辑表,将交由 ShardingSphere 主动治理分片,用户只须要指定分片数量和应用的数据库实例,无需再关怀表的具体散布,配置格局如下:

spring:
  shardingsphere:
    # 数据源配置
    datasource:
      ......
    # 具体规定配置
    rules:
      sharding:
        # 逻辑表分片规定
        tables:
          # 逻辑表名称
          t_order:
            .....
        # 主动分片表规定配置
        auto-tables:
          t_order: # 逻辑表名称
            actual-data-sources: db$->{0..1}
            sharding-strategy: # 切分策略
              standard: # 用于单分片键的规范分片场景
                sharding-column: order_id # 分片列名称
                sharding-algorithm-name: t_order_mod # 主动分片算法名称

ShardingSphere-Jdbc中配置应用 auto-tables 次要两个参数,actual-data-sources指定数据源散布,因为是治理分片表所以只需数据源信息即可;sharding-strategy指具体采纳何种算法来进行分片。

对逻辑表的 DDL 操作,零碎会首先查看是否配置了AutoTable,如果已配置,则优先采纳配置的规定;若未配置,则将应用默认的逻辑表分片规定。

AutoTable反对 ShardingSphere 内置的全副主动分片算法,所谓主动分片算法就是依据 actualDataSources 设置的数据源信息,应用对应内置算法自行解析解决。

  • MOD:取模分片算法
  • HASH_MOD:哈希取模分片算法
  • VOLUME_RANGE:基于分片容量的范畴分片算法
  • BOUNDARY_RANGE:基于分片边界的范畴分片算法
  • AUTO_INTERVAL:主动时间段分片算法

AutoTable 应用

举个例子,咱们应用内置 MOD 取模算法作为 AutoTable 的分片算法,同样是 db0db1 两个实例中各创立 1000 张分片表。那么当对逻辑表的 DDL 操作时,ShardingSphere会根据分片表编号 t_order_0~t_order_1999 % 数据库实例数 取模来确认 DDL 命令路由到哪个实例中执行。


spring:
  shardingsphere:
    # 数据源配置
    datasource:
      .....
    # 具体规定配置
    rules:
      sharding:
        # 主动分片表规定配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_mod
        # 分片算法定义
        sharding-algorithms:
          t_order_mod:
            type: MOD
            props:
              # 指定分片数量
              sharding-count: 2000

还是执行方才创立表的单元测试,会发现 db0db1 两个实例中曾经各自创立了 1000 张分片表,但你会发现 1000 张表曾经不再是依照程序创立的了。

上边应用的是内置主动分片算法,它对于咱们来说是黑盒,提供它不便咱们拿来即用。不过,如果想要做到更细粒度的治理分片表,最好的方法就是自定义分片算法,后续章节会介绍所有内置分片算法和自定义分片算法的应用

总结

在应用 ShardingSphere 实现分库分表的时候,要摒弃 先建表、再配规定 的传统思维,要先确定规定在建表,治理表是一件很简略的事,咱们只有通知 ShardingSphere 分片数量和散布规定,剩下的就让框架来解决就好了。

本文案例代码 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-autocreate-table

我是小富~ 下期见

退出移动版