最近在学习使用 shardingsphere 分表,定位为轻量级 Java 框架,无需部署额外服务,引入 Jar 包即可使用;当前使用发布最新版本 4.0.0,新特性查看:ShardingSphere 4.0.0
pom 文件引入maven依赖:
org.apache.shardingsphere
sharding-jdbc-spring-boot-starter
4.0.0
com.alibaba
druid
${druid-version}
YML 文件配置(也可以使用 Java 代码配置):
1. 数据源配置
spring:
shardingsphere:
# 数据源配置
datasource:
names: ds0 # ,ds1
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ds0name
username: root
password: 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_id
type: 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: id
type: 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 {
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
// 逻辑表名称
String logicTableName = preciseShardingValue.getLogicTableName();
// cure_time = preciseShardingValue.getValue();
String cure_time = preciseShardingValue.getValue();
// 将时间字符串转换为日期类型
LocalDate parseDate = LocalDate.parse(cure_time, dtfTime);
// 获取日期 yyyyMMdd
String 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 {
@Override
public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) {
// 逻辑表名称
String logicTableName = rangeShardingValue.getLogicTableName();
Range 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 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 getRangeTables(String logicTableName,LocalDateTime startTime, LocalDateTime endTime) {
Collection 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;
}
关于查询:
遇到问题记录:
ShardingSphere 4.0.0-RC3 之前的版本 MySQL NOW() 被解析为字符串 "NOW()",导致保存时间字段失败
ShardingSphere 4.0.0 + druid-spring-boot-starter 启动初始化数据源找不到 "url",具体原因:可能是 druid-spring-boot-starter 会去构建数据源,换为 druid 即可解决