当前位置: 代码迷 >> 综合 >> 使用 ShardingSphere 分表学习笔记
  详细解决方案

使用 ShardingSphere 分表学习笔记

热度:13   发布时间:2024-01-25 04:35:28.0

最近在学习使用 shardingsphere 分表,定位为轻量级 Java 框架,无需部署额外服务,引入 Jar 包即可使用;当前使用发布最新版本 4.0.0,新特性查看:ShardingSphere 4.0.0 

pom 文件引入maven依赖

<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.0.0</version>
</dependency><!--<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid-version}</version>
</dependency>--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid-version}</version>
</dependency>

<druid-version>1.1.10</druid-version> ,这里使用的是 sharding-jdbc-spring-boot-starter,也可以使用 sharding-jdbc-core,版本号一致即可

 

YML 文件配置(也可以使用 Java 代码配置):

     1. 数据源配置

spring:shardingsphere:# 数据源配置datasource:names: ds0  # ,ds1 ds0:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/ds0nameusername: rootpassword: xxxx# other params# 多数据源#ds1:#  type: com.alibaba.druid.pool.DruidDataSource#  driver-class-name: com.mysql.jdbc.Driver#  url: jdbc:mysql://localhost:3306/ds1name#  username: root#  password: xxxx

我配置的是单数据源分表,也可指定多数据源进行分库策略 or 主从策略

 

      2. 表分片策略

          2.1 主键/ID字段分片

    sharding:tables:sys_user: # 逻辑表actual-data-nodes: ds0.sys_user_$->{0..9} # 表数量配置table-strategy:inline:sharding-column: user_id # 分片键algorithm-expression: sys_user_$->{user_id % 10} # 根据 user_id 取模分表key-generator: #  user_id 生成策略column: user_idtype: SNOWFLAKE#  sys_xxx # 其他表分片

sys_user 是逻辑表名称,分表后创建的表应该是 sys_user_number

actual-data-nodes 是数据节点由数据源和数据表组成,也就是真实表,使用 行表达式  {0..10} 表示有 sys_user_0 到 sys_user_9 共 十张表;shardingsphere 不会自动创建表,需要使用 脚本定时 或 手动提前创建好

table-strategy 是 分片策略,根据需求实现具体的分片策略,inline 为行表达式,standard 为自定义分片

algorithm-expression 是算法表达式,根据 user_id 取模尾数为 n 的路由到后缀为 n 的表中(sys_user_n)

key-generator 是主键生成,SNOWFLAKE 是Twitter的分布式 ID 生成算法

          2.2 时间字段分片(如日志表)

sharding:tables:sys_log: # 逻辑表actual-data-nodes: ds0.sys_log_20200116 # 默认表配置table-strategy:standard:sharding-column: log_time # 分片列preciseAlgorithmClassName: pers.allen.demo.config.SysLogDataTableShardingAlgorithm # 精确分片rangeAlgorithmClassName:  pers.allen.demo.config.SysLogDataTableRangeShardingAlgorithm # 范围分片key-generator: #  id 生成策略column: idtype: SNOWFLAKE

preciseAlgorithmClassName 和 rangeAlgorithmClassName 为分片支持和实现请查看 分片算法 ,下面是按天分片实现,按月分片只需要改动部分代码即可

代码如下:

/*** 通用部分*/
public class SysLogDataTableSharding {//protected static final String UNDERLINE = "_";// cure_time 日期格式protected static final DateTimeFormatter dtfTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");protected static final DateTimeFormatter dtfDate = DateTimeFormatter.ofPattern("yyyyMMdd");protected static String spliceTableName(String logicTableName,String date) {StringBuilder tableName = new StringBuilder();tableName.append(logicTableName).append(UNDERLINE).append(date);return tableName.toString();}}

 

/*** 精确分片* PreciseShardingAlgorithm:用于处理使用单一键作为分片键的=与IN进行分片的场景* Created by lengyul on 2020/1/8 10:11*/
public class SysLogDataTableShardingAlgorithm extends SysLogDataTableSharding implements PreciseShardingAlgorithm<String> {@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {// 逻辑表名称String logicTableName = preciseShardingValue.getLogicTableName();// cure_time = preciseShardingValue.getValue();String cure_time = preciseShardingValue.getValue();// 将时间字符串转换为日期类型LocalDate parseDate = LocalDate.parse(cure_time, dtfTime);// 获取日期 yyyyMMddString yyyyMMdd = parseDate.format(dtfDate);// 实际表名称String realTableName = spliceTableName(logicTableName,yyyyMMdd);System.out.println(realTableName);return realTableName;}}
/*** 范围分片* RangeShardingAlgorithm:用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景* Created by lengyul on 2020/1/8 15:06*/
public class SysLogDataTableRangeShardingAlgorithm extends SysLogDataTableSharding implements RangeShardingAlgorithm<String> {@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {// 逻辑表名称String logicTableName = rangeShardingValue.getLogicTableName();Range<String> ranges = rangeShardingValue.getValueRange();// 获取时间范围try {String lower = ranges.lowerEndpoint();String upper = ranges.upperEndpoint();LocalDateTime startTime = LocalDateTime.parse(lower,dtfTime);LocalDateTime endTime = LocalDateTime.parse(upper,dtfTime);Collection<String> rangeTables = getRangeTables(logicTableName,startTime, endTime);System.out.println(rangeTables);return rangeTables;} catch (Exception e) {e.printStackTrace();;}return collection;}/*** LocalDateTime 计算时间表范围* @param logicTableName* @param startTime* @param endTime* @return*/private static Collection<String> getRangeTables(String logicTableName,LocalDateTime startTime, LocalDateTime endTime) {Collection<String> tables = new LinkedHashSet<>();while(startTime.isBefore(endTime)) {String yyyyMMdd = startTime.toLocalDate().format(dtfDate);String realTableName = spliceTableName(logicTableName,yyyyMMdd);tables.add(realTableName);startTime = startTime.plusDays(1);}/*当 startTime 的 HH:mm:ss 大于 endTime 时,day + 1 会出现 startTime 大于 endTime导致 endTime 对应的表没有添加到 tables 列表中 */String endDate = endTime.toLocalDate().format(dtfDate);String lastTable = spliceTableName(logicTableName,endDate);if(!tables.contains(lastTable)) {tables.add(lastTable);}return tables;}

关于查询:

  •  分片后,如果查询 where 条件没有带分片字段的话会去扫描配置的所有真实数据表 = actual-data-nodes,最后将匹配到的数据合并为一个结果集返回,保存数据时找不到对应的表会报表不存在
  •  复杂SQL或者UNION可能会不支持,具体 SQL使用规范

 

遇到问题记录:

ShardingSphere 4.0.0-RC3 之前的版本 MySQL NOW()   被解析为字符串 "NOW()",导致保存时间字段失败

ShardingSphere 4.0.0 + druid-spring-boot-starter 启动初始化数据源找不到 "url",具体原因:可能是 druid-spring-boot-starter 会去构建数据源,换为 druid 即可解决