sharding-jdbc-springboot简单样例

    科技2022-09-02  98

    ShardingSphere官方文档

    ShardingSphere

    样例源代码

    sharding-jdbc-springboot

    1,sharding-jdbc 基本概念

    逻辑表 :水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为 10 张表,分别是 t_order_0 到 t_order_9,他们的逻辑表名为 t_order。

    真实表:数据库中真实存在的物理表。即上个示例中的 t_order_0 到 t_order_9。

    数据节点:数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0。

    绑定表:分片规则一致的主表和子表。例如:t_order 表和 t_order_item 表,均按照 order_id 分片,则此两张表互为绑定表关系。

    广播表:指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。例如:字典表。

    分片键:将数据库(表)水平拆分的关键字段

    分片算法:

    通过分片算法将数据分片。

    StandardShardingAlgorithm:标准分片算法,用于处理使用单一键作为分片键的 =、IN、BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合 StandardShardingStrategy 使用。

    ComplexKeysShardingAlgorithm :复合分片算法,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合 ComplexShardingStrategy 使用。

    HintShardingAlgorithm :Hint分片算法,用于处理使用 Hint 行分片的场景。需要配合 HintShardingStrategy 使用。

    分片策略:

    包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供 5 种分片策略。

    StandardShardingStrategy:”标准分片策略。提供对 SQ L语句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持。 StandardShardingStrategy 只支持单分片键,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 两个分片算法。 PreciseShardingAlgorithm 是必选的,用于处理 = 和 IN 的分片。 RangeShardingAlgorithm 是可选的,用于处理 BETWEEN AND, >, <, >=, <=分片,如果不配置 RangeShardingAlgorithm,SQL 中的 BETWEEN AND 将按照全库路由处理。

    ComplexShardingStrategy:复合分片策略。提供对 SQL 语句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持。 ComplexShardingStrategy 支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。

    HintShardingStrategy:Hint分片策略,通过 Hint 指定分片值而非从 SQL 中提取分片值的方式进行分片的策略。

    NoneShardingStrategy:不分片策略

    行表达式

    行表达式的使用非常直观,只需要在配置中使用 ${ expression } 或 $->{ expression } 标识行表达式即可。 目前支持数据节点和分片算法这两个部分的配置。行表达式的内容使用的是 Groovy 的语法,Groovy 能够支持的所有操作,行表达式均能够支持。例如:

    ${begin…end} 表示范围区间

    ${[unit1, unit2, unit_x]} 表示枚举值

    例如,以下行表达式:

    $->{['online', 'offline']}_table$->{1..3}

    最终会解析为:

    online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3

    2,实例

    1,引入jar包

    <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.12</version> </dependency> <!--数据库--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.1.1</version> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency> <!--aspect--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.6</version> </dependency> <!--validation--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.11</version> </dependency> </dependencies>

    2,配置

    application.yml 文件

    server: port: 8081 spring: shardingsphere: props: sql: # 打印sql show: true orchestration: registry: #操作超时的毫秒数,默认500毫秒 operation-timeout-milliseconds: 600000 datasource: names: ds0,ds0slave,ds1,ds1slave ds0: driverClassName: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource jdbcUrl: jdbc:mysql://192.168.16.128:3306/ds0?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=UTC username: root password: 123456 # hikari 的线程池配置 maximumPoolSize: 20 minimumIdle: 10 ds0slave: driverClassName: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource jdbcUrl: jdbc:mysql://192.168.16.129:3306/ds0?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=UTC username: root password: 123456 # hikari 的线程池配置 maximumPoolSize: 20 minimumIdle: 10 ds1: driverClassName: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource jdbcUrl: jdbc:mysql://192.168.16.128:3306/ds1?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=UTC username: root password: 123456 # hikari 的线程池配置 maximumPoolSize: 20 minimumIdle: 10 ds1slave: driverClassName: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource jdbcUrl: jdbc:mysql://192.168.16.129:3306/ds1?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=UTC username: root password: 123456 # hikari 的线程池配置 maximumPoolSize: 20 minimumIdle: 10 sharding: #未分片表的默认数据库 default-data-source-name: ds0 #读写分离 master-slave-rules: ds0: master-data-source-name: ds0 slave-data-source-names: ds0slave ds1: master-data-source-name: ds1 slave-data-source-names: ds1slave tables: #分表案例 users: actual-data-nodes: ds0.users_$->{0..1} key-generator: column: id type: SNOWFLAKE table-strategy: inline: sharding-column: id algorithm-expression: users_$->{id % 2} #分表分库案例 user_pack_records: actual-data-nodes: ds0.user_pack_records_$->{2018..2019},ds1.user_pack_records_$->{2020..2021} key-generator: column: id type: SNOWFLAKE database-strategy: standard: sharding-column: start_time precise-algorithm-class-name: com.sharding.jdbc.algorithm.db.UserPackRecordsAlgorithm range-algorithm-class-name: com.sharding.jdbc.algorithm.db.UserPackRecordsAlgorithm table-strategy: standard: sharding-column: start_time precise-algorithm-class-name: com.sharding.jdbc.algorithm.table.UserPackRecordsAlgorithm range-algorithm-class-name: com.sharding.jdbc.algorithm.table.UserPackRecordsAlgorithm mybatis: configuration: #驼峰转化 map-underscore-to-camel-case: true # Java 与 Mysql 的类型转化基础包 type-handlers-package: com.sharding.jdbc.convert

    3,自定义数据库分片算法

    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * user_pack_records 分库:阅读时间 start_time 在2018-2019 在 db0,2020-2021 在db1 */ public class UserPackRecordsAlgorithm implements PreciseShardingAlgorithm<LocalDateTime>, RangeShardingAlgorithm<LocalDateTime> { /** * 通过年份获取真实数据库 * * @param year 年份 * @return 真实数据库 */ private String getDbByYear(Integer year) { if (year == 2018 || year == 2019) { return "ds0"; } else if (year == 2020 || year == 2021) { return "ds1"; } throw new IllegalArgumentException(); } /** * PreciseShardingAlgorithm 是必选的,用于处理=和IN的分片。 * * @param availableDbNames 可用的数据库集合 * @param shardingValue 分片键 * @return 真实数据库 */ @Override public String doSharding(Collection<String> availableDbNames, PreciseShardingValue<LocalDateTime> shardingValue) { int year = shardingValue.getValue().getYear(); return getDbByYear(year); } /** * RangeShardingAlgorithm 可选,用于处理BETWEEN AND, >, <, >=, <=分片 * * @param availableDbNames 可用的数据库集合 * @param rangeShardingValue 分片键 * @return 真实数据库 */ @Override public Collection<String> doSharding(Collection<String> availableDbNames, RangeShardingValue<LocalDateTime> rangeShardingValue) { List<String> dbs = new ArrayList<>(); int lowerYear = rangeShardingValue.getValueRange().lowerEndpoint().toLocalDate().getYear(); LocalDate startDate = LocalDate.of(lowerYear, 1, 1); LocalDate endDate = rangeShardingValue.getValueRange().upperEndpoint().toLocalDate(); for (LocalDate curDate = startDate; curDate.isBefore(endDate) || curDate.isEqual(endDate); curDate = curDate.plusYears(1)) { dbs.add(getDbByYear(curDate.getYear())); } return dbs; } }

    自定义分表算法

    package com.sharding.jdbc.algorithm.table; import org.apache.commons.lang3.StringUtils; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class UserPackRecordsAlgorithm implements PreciseShardingAlgorithm<LocalDateTime>, RangeShardingAlgorithm<LocalDateTime> { /** * 通过年份获取真实表 * * @param year 年份 * @return 真实表 */ private String getTableByYear(Collection<String> availableTargetNames, String year) { for (String targetName : availableTargetNames) { if (targetName.endsWith(year)) { return targetName; } } return null; } /** * PreciseShardingAlgorithm 是必选的,用于处理=和IN的分片。 * * @param availableTargetNames 可用的表集合 * @param shardingValue 分片键 * @return 真实表 */ @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<LocalDateTime> shardingValue) { LocalDateTime startTime = shardingValue.getValue(); return getTableByYear(availableTargetNames, String.valueOf(startTime.getYear())); } /** * RangeShardingAlgorithm 可选,用于处理BETWEEN AND, >, <, >=, <=分片 * * @param availableTargetNames 可用的表集合 * @param rangeShardingValue 分片键 * @return 真实表 */ @Override public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<LocalDateTime> rangeShardingValue) { List<String> tables = new ArrayList<>(); int lowerYear = rangeShardingValue.getValueRange().lowerEndpoint().toLocalDate().getYear(); LocalDate startDate = LocalDate.of(lowerYear, 1, 1); LocalDate endDate = rangeShardingValue.getValueRange().upperEndpoint().toLocalDate(); for (LocalDate curDate = startDate; curDate.isBefore(endDate) || curDate.isEqual(endDate); curDate = curDate.plusYears(1)) { String table = getTableByYear(availableTargetNames, String.valueOf(curDate.getYear())); if (StringUtils.isNotBlank(table)) { tables.add(table); } } return tables; } }

    4,详细代码请进入githut看源代码 sharding-jdbc-springboot

    Processed: 0.009, SQL: 9