大家好,我是小富~

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

/** * 公众号:程序员小富 */@Configurationpublic 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 */@SpringBootTestclass 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 */@SpringBootTestclass 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

我是小富~ 下期见