内置算法
https://shardingsphere.apache.org/document/5.2.0/cn/user-manual/common-config/builtin-algorithm/sharding/
官网介绍已经很清楚了,但是官网的例子不多,下面是使用时间分片算法的具体例子:
Properties properties = new Properties();
//分片键的时间戳格式,必须遵循 Java DateTimeFormatter 的格式 识别数据
properties.setProperty("datetime-pattern", dateTimePattern);
//设置 起始时间 当前时间 2024-03-27 时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致
properties.setProperty("datetime-lower", dateTimeLower);
//设置 截止时间
properties.setProperty("datetime-upper", dateTimeUpper);
//对应数据库表后缀名202211
properties.setProperty("sharding-suffix-pattern", suffixPattern);
//设置时间间隔
properties.setProperty("datetime-interval-unit", "MONTHS"); //MONTHS DAYS YEARS
//设置时间间隔数量
properties.setProperty("datetime-interval-amount", "1");
shardingAlgorithms.put(shardingAlgorithmName, new AlgorithmConfiguration("INTERVAL", properties));
自定义算法
自定义算法支持2种配置:
首先实现自定义算法类:
@Getter
@CommonsLog
@Component
public class HisDataMonthShardingAlgorithm implements StandardShardingAlgorithm<String> {
private Properties props;
/**
* 设置该参数的原因是,如果在范围查找的时候我们没有设置最小值,比如下面的查询
* where acquisition_time < '2022-08-11 00:00:00'
* 这个时候范围查找就只有上限而没有下限,这时候就需要有一个下限值兜底,不能一致遍历下去
*/
private LocalDate tableLowerDate;
private String suffixPattern;
private ChronoUnit chronoUnit;
/**
* 在配置文件中配置算法的时候会配置 props 参数,框架会将props中的配置放在 properties 参数中,并且初始化算法的时候被调用
*
* @param properties properties
*/
@Override
public void init(Properties properties) {
this.props = properties;
String dateTimeLower = properties.getProperty("dateTimeLower");
suffixPattern = properties.getProperty("suffixPattern");
chronoUnit = ChronoUnit.valueOf(properties.getProperty("intervalUnit"));
try {
switch (chronoUnit) {
case DAYS:
tableLowerDate = LocalDate.of(Integer.parseInt(dateTimeLower.split("-")[0]), Integer.parseInt(dateTimeLower.split("-")[1]), Integer.parseInt(dateTimeLower.split("-")[2]));
break;
case MONTHS:
tableLowerDate = LocalDate.of(Integer.parseInt(dateTimeLower.split("-")[0]), Integer.parseInt(dateTimeLower.split("-")[1]), 1);
break;
case YEARS:
tableLowerDate = LocalDate.of(Integer.parseInt(dateTimeLower), 1, 1);
break;
}
} catch (NumberFormatException e) {
log.error("解析时间出错,返回当前时间", e);
//解析出错,返回当前时间
tableLowerDate = LocalDate.now();
}
}
/**
* 精确路由算法
*
* @param availableTargetNames 可用的表列表(配置文件中配置的 actual-data-nodes会被解析成 列表被传递过来)
* @param shardingValue 精确的值
* @return 结果表
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
String value = shardingValue.getValue();
// 根据精确值获取路由表
String actuallyTableName = shardingValue.getLogicTableName() + "_" + getYYYYMM(value);
if (availableTargetNames.contains(actuallyTableName)) {
return actuallyTableName;
}
return null;
}
public String getYYYYMM(String dateString) {
LocalDate date = LocalDate.parse(dateString.split(" ")[0], DateTimeFormatter.ISO_LOCAL_DATE);
return date.format(DateTimeFormatter.ofPattern(suffixPattern));
}
/**
* 范围路由算法
*
* @param availableTargetNames 可用的表列表(配置文件中配置的 actual-data-nodes会被解析成 列表被传递过来)
* @param shardingValue 值范围
* @return 路由后的结果表
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<String> shardingValue) {
// 获取到范围查找的最小值,如果条件中没有最小值设置为 tableLowerDate
LocalDate rangeLowerDate = null;
if (shardingValue.getValueRange().hasLowerBound()) {
rangeLowerDate = LocalDate.parse(shardingValue.getValueRange().lowerEndpoint().split(" ")[0], DateTimeFormatter.ISO_LOCAL_DATE);
} else {
rangeLowerDate = tableLowerDate;
}
// 获取到范围查找的最大值,如果没有配置最大值,设置为当前 月
LocalDate rangeUpperDate;
if (shardingValue.getValueRange().hasUpperBound()) {
rangeUpperDate = LocalDate.parse(shardingValue.getValueRange().upperEndpoint().split(" ")[0], DateTimeFormatter.ISO_LOCAL_DATE);
} else {
rangeUpperDate = LocalDate.now();
}
List<String> tableNames = new ArrayList<>();
// 过滤那些存在的表
while (rangeLowerDate.isBefore(rangeUpperDate) || rangeLowerDate.isEqual(rangeUpperDate)) {
String actuallyTableName = shardingValue.getLogicTableName() + "_" + rangeLowerDate.format(DateTimeFormatter.ofPattern(suffixPattern));
if (availableTargetNames.contains(actuallyTableName)) {
tableNames.add(actuallyTableName);
}
rangeLowerDate = rangeLowerDate.plus(1, chronoUnit);
}
if (tableNames.isEmpty()) {
log.warn("未匹配任何表");
return availableTargetNames;
}
return tableNames;
}
/**
* SPI方式的 SPI名称,配置文件中配置的时候需要用到
*/
@Override
public String getType() {
return "HIS_DATA_SPI_BASED";
}
全限类名
// 自定义算法 使用全限类名
Properties properties = new Properties();
properties.setProperty("strategy","standard");
properties.setProperty("dateTimeLower",dateTimeLower);
properties.setProperty("algorithmClassName","com.antaiib.custom.sharding.util.HisDataMonthShardingAlgorithm");
shardingAlgorithms.put(shardingAlgorithmName, new AlgorithmConfiguration("CLASS_BASED", properties));
SPI
需要在resources
目录下新建目录 META-INF\services,新建文件:
org.apache.shardingsphere.sharding.spi.ShardingAlgorithm
(为什么文件名是这个,因为自定义算法就是实现这个接口)
在文件中写入刚刚实现算法的全限类名:
com.example.sharding.util.HisDataMonthShardingAlgorithm
java API配置如下:
// 自定义算法 使用SPI
Properties properties = new Properties();
properties.setProperty("dateTimeLower", dateTimeLower);
properties.setProperty("intervalUnit", intervalUnit);
properties.setProperty("suffixPattern", suffixPattern);
shardingAlgorithms.put(shardingAlgorithmName, new AlgorithmConfiguration("HIS_DATA_SPI_BASED", properties));