基于原有项目 对check_log push_log 和 receives表根据日期字段(字段类型为varchar) 按日分表。
以check_log 为例,以下是check_date格式,注意他是varchar

正因为他是varchar 类型 ,所有需要用到精准分片和范围分片,配置文件如下。
配置文件
spring.shardingsphere.enabled=true
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true
spring.shardingsphere.datasource.names=sharding
spring.shardingsphere.datasource.sharding.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.sharding.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.sharding.url=jdbc:mysql://localhost:3306/test
spring.shardingsphere.datasource.sharding.username=root
spring.shardingsphere.datasource.sharding.password=mima
spring.shardingsphere.sharding.tables.check_log.actual-data-nodes=sharding.check_log
spring.shardingsphere.sharding.tables.check_log.table-strategy.standard.sharding-column=check_date
spring.shardingsphere.sharding.tables.check_log.table-strategy.standard.precise-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring.shardingsphere.sharding.tables.check_log.table-strategy.standard.range-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring.shardingsphere.sharding.tables.push_log.actual-data-nodes=sharding.push_log
spring.shardingsphere.sharding.tables.push_log.table-strategy.standard.sharding-column=push_date
spring.shardingsphere.sharding.tables.push_log.table-strategy.standard.precise-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring.shardingsphere.sharding.tables.push_log.table-strategy.standard.range-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring.shardingsphere.sharding.tables.receives.actual-data-nodes=sharding.receives
spring.shardingsphere.sharding.tables.receives.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.sharding.tables.receives.table-strategy.standard.precise-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring.shardingsphere.sharding.tables.receives.table-strategy.standard.range-algorithm-class-name=com.example.demo.test.TimeShardingAlgorithm
spring 工具类
package com.example.demo.test;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* <p> @Title SpringUtil
* <p> @Description Spring工具类
*/
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return SpringUtil.applicationContext;
}
public static <T> T getBean(Class<T> cla) {
return applicationContext.getBean(cla);
}
public static <T> T getBean(String name, Class<T> cal) {
return applicationContext.getBean(name, cal);
}
public static String getProperty(String key) {
return applicationContext.getBean(Environment.class).getProperty(key);
}
}
分片表缓存枚举类
package com.example.demo.test;
import java.util.*;
/**
* <p> @Title ShardingTableCacheEnum
* <p> @Description 分片表缓存枚举
*/
public enum ShardingTableCacheEnum {
CHECKLOG("check_log", new HashSet<>()),
PUSHLOG("push_log", new HashSet<>()),
RECEIVES("receives", new HashSet<>());
/**
* 逻辑表名
*/
private final String logicTableName;
/**
* 实际表名
*/
private final Set<String> resultTableNamesCache;
private static Map<String, ShardingTableCacheEnum> valueMap = new HashMap<>();
static {
Arrays.stream(ShardingTableCacheEnum.values()).forEach(o -> valueMap.put(o.logicTableName, o));
}
ShardingTableCacheEnum(String logicTableName, Set<String> resultTableNamesCache) {
this.logicTableName = logicTableName;
this.resultTableNamesCache = resultTableNamesCache;
}
public static ShardingTableCacheEnum of(String value) {
return valueMap.get(value);
}
public String logicTableName() {
return logicTableName;
}
public Set<String> resultTableNamesCache() {
return resultTableNamesCache;
}
public static Set<String> logicTableNames() {
return valueMap.keySet();
}
@Override
public String toString() {
return "ShardingTableCacheEnum{" +
"logicTableName='" + logicTableName + '\'' +
", resultTableNamesCache=" + resultTableNamesCache +
'}';
}
}
分片规则实现类
package com.example.demo.test;
import cn.hutool.core.date.DateUtil;
import com.example.demo.utils.DateUtils;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
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.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
/**
* 按天分片
*/
@Slf4j
public class TimeShardingAlgorithm implements PreciseShardingAlgorithm<String>, RangeShardingAlgorithm<String> {
/**
* 精准分片
*
* @param tableNames 对应分片库中所有分片表的集合
* @param preciseShardingValue 分片键值,其中 logicTableName 为逻辑表,columnName 分片键,value 为从 SQL 中解析出来的分片键的值
* @return 表名
*/
@Override
public String doSharding(Collection<String> tableNames, PreciseShardingValue<String> preciseShardingValue) {
String logicTableName = preciseShardingValue.getLogicTableName();
ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
if (logicTable == null) {
log.error(">>>>>>>>>> 【ERROR】数据表类型错误,请稍后重试,logicTableNames:{},logicTableName:{}",
ShardingTableCacheEnum.logicTableNames(), logicTableName);
throw new IllegalArgumentException("数据表类型错误,请稍后重试");
}
log.info(">>>>>>>>>> 【INFO】精确分片,节点配置表名:{},数据库缓存表名:{}", tableNames, logicTable.resultTableNamesCache());
String dateTime = preciseShardingValue.getValue();
String date = dateTime.replaceAll("-", "_");
String resultTableName = logicTableName + "_" + date;
// 检查分表获取的表名是否存在,不存在则自动建表
return ShardingAlgorithmTool.getShardingTableAndCreate(logicTable.logicTableName(), resultTableName);
}
/**
* 范围分片
*
* @param tableNames 对应分片库中所有分片表的集合
* @param rangeShardingValue 分片范围
* @return 表名集合
*/
@Override
public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<String> rangeShardingValue) {
String logicTableName = rangeShardingValue.getLogicTableName();
ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
if (logicTable == null) {
log.error(">>>>>>>>>> 【ERROR】逻辑表范围异常,请稍后重试,logicTableNames:{},logicTableName:{}",
ShardingTableCacheEnum.logicTableNames(), logicTableName);
throw new IllegalArgumentException("逻辑表范围异常,请稍后重试");
}
log.info(">>>>>>>>>> 【INFO】范围分片,节点配置表名:{},数据库缓存表名:{}", tableNames, logicTable.resultTableNamesCache());
// between and 的起始值
Range<String> valueRange = rangeShardingValue.getValueRange();
boolean hasLowerBound = valueRange.hasLowerBound();
boolean hasUpperBound = valueRange.hasUpperBound();
// 获取最大值和最小值
Set<String> tableNameCache = logicTable.resultTableNamesCache();
String min = hasLowerBound ? valueRange.lowerEndpoint() :getLowerEndpoint(tableNameCache);
String max = hasUpperBound ? valueRange.upperEndpoint() :getUpperEndpoint(tableNameCache);
// 循环计算分表范围
Set<String> resultTableNames = new LinkedHashSet<>();
while (min.compareTo(max)<0 || min.equals(max)) {
String temp = min.replaceAll("-", "_");
String tableName = logicTableName + "_" + temp;
resultTableNames.add(tableName);
Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(min);
} catch (ParseException e) {
throw new RuntimeException(e);
}
min = DateUtil.offsetDay(date, 1).toString("yyyy-MM-dd");
}
// 循环计算分表范围
return ShardingAlgorithmTool.getShardingTablesAndCreate(logicTable.logicTableName(), resultTableNames);
}
/**
* 获取 最小分片值
* @param tableNames 表名集合
* @return 最小分片值
*/
private String getLowerEndpoint(Collection<String> tableNames) {
Optional<Date> optional = tableNames.stream()
.map(o -> DateUtils.dateTime("DATE_TIME_FORMATTER_STRING",o.replace("TABLE_SPLIT_SYMBOL", "") + "01 00:00:00"))
.min(Comparator.comparing(Function.identity()));
if (optional.isPresent()) {
return DateUtil.offsetDay(optional.get(), 1).toString("yyyy-MM-dd");
} else {
log.error("====> 【ERROR】获取数据最小分表失败,请稍后重试,tableName:{}", tableNames);
throw new IllegalArgumentException("获取数据最小分表失败,请稍后重试");
}
}
/**
* 获取 最大分片值
* @param tableNames 表名集合
* @return 最大分片值
*/
private String getUpperEndpoint(Collection<String> tableNames) {
Optional<Date> optional = tableNames.stream()
.map(o -> DateUtils.dateTime("DATE_TIME_FORMATTER_STRING",o.replace("TABLE_SPLIT_SYMBOL", "") + "01 00:00:00"))
.max(Comparator.comparing(Function.identity()));
if (optional.isPresent()) {
return DateUtil.offsetDay(optional.get(), 1).toString("yyyy-MM-dd");
} else {
log.error("====> 【ERROR】获取数据最大分表失败,请稍后重试,tableName:{}", tableNames);
throw new IllegalArgumentException("获取数据最大分表失败,请稍后重试");
}
}
}
分片算法工具
package com.example.demo.test;
import cn.hutool.core.collection.ConcurrentHashSet;
import com.alibaba.druid.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.core.rule.DataNode;
import org.apache.shardingsphere.core.rule.TableRule;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.datasource.ShardingDataSource;
import org.springframework.core.env.Environment;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p> @Title ShardingAlgorithmTool
* <p> @Description 按天分片算法工具
*/
@Slf4j
public class ShardingAlgorithmTool {
private static volatile ShardingAlgorithmTool instance;
/**
* 逻辑表名,例:order
*/
private final static List<String> LOGIC_TABLE_NAME
= new ArrayList<>(Arrays.asList(
"check_log",
"push_log",
"receives"
));
/**
* 表分片符号,例:order_20240808 中,分片符号为 "_"
*/
private static final String TABLE_SPLIT_SYMBOL = "_";
/**
* 已存在表名集合缓存
*/
private static final Set<String> TABLE_NAME_CACHE = new ConcurrentHashSet<>();
/**
* 数据库配置
*/
private static final Environment ENV = SpringUtil.getApplicationContext().getEnvironment();
private static final String DATASOURCE_URL = ENV.getProperty("spring.shardingsphere.datasource.sharding.url");
private static final String DATASOURCE_USERNAME = ENV.getProperty("spring.shardingsphere.datasource.sharding.username");
private static final String DATASOURCE_PASSWORD = ENV.getProperty("spring.shardingsphere.datasource.sharding.password");
private ShardingAlgorithmTool(){
}
public static ShardingAlgorithmTool getInstance(){
if (instance == null) {
synchronized (ShardingAlgorithmTool.class) {
if (instance == null) {
instance = new ShardingAlgorithmTool();
}
}
}
return instance;
}
/**
* 检查分表获取的表名是否存在,不存在则自动建表
*
* @param logicTableName 逻辑表名,例:order
* @param resultTableName 真实表名,例:order_20240808
* @return 确认存在于数据库中的真实表名
*/
public static String getShardingTableAndCreate(String logicTableName, String resultTableName) {
// 缓存中有此表则返回,没有则判断创建
if (TABLE_NAME_CACHE.contains(resultTableName)) {
return resultTableName;
} else {
// 未创建的表返回逻辑空表
boolean isSuccess = createShardingTable(logicTableName, resultTableName);
return isSuccess ? resultTableName : logicTableName;
}
}
/**
* 检查分表获取的表名是否存在,不存在则自动建表
*
* @param logicTableName 逻辑表名,例:order
* @param resultTableNames 真实表名,例:order_20240808
* @return 存在于数据库中的真实表名集合
*/
public static Set<String> getShardingTablesAndCreate(String logicTableName, Collection<String> resultTableNames) {
return resultTableNames.stream().map(o -> getShardingTableAndCreate(logicTableName, o)).collect(Collectors.toSet());
}
/**
* 缓存重载
*/
public static void tableNameCacheReload() {
// 读取数据库中|所有表名
List<String> tableNameList = getAllTableNameBySchema();
// 删除旧的缓存(如果存在)
TABLE_NAME_CACHE.clear();
// 写入新的缓存
TABLE_NAME_CACHE.addAll(tableNameList);
// 动态更新配置 actualDataNodes
actualDataNodesRefresh();
}
/**
* 获取所有表名
*
* @return 表名集合
*/
public static List<String> getAllTableNameBySchema() {
List<String> tableNames = new ArrayList<>();
if (StringUtils.isEmpty(DATASOURCE_URL) || StringUtils.isEmpty(DATASOURCE_USERNAME) || StringUtils.isEmpty(DATASOURCE_PASSWORD)) {
log.error(">>>>>>>>>> 【ERROR】数据库连接配置有误,请稍后重试,URL:{}, username:{}, password:{}", DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
throw new IllegalArgumentException("数据库连接配置有误,请稍后重试");
}
try (Connection conn = DriverManager.getConnection(DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
Statement st = conn.createStatement()) {
for (int i=0;i<LOGIC_TABLE_NAME.size();i++){
try (ResultSet rs = st.executeQuery("show TABLES like '" + LOGIC_TABLE_NAME.get(i) + TABLE_SPLIT_SYMBOL + "%'")) {
while (rs.next()) {
tableNames.add(rs.getString(1));
}
}
}
} catch (SQLException e) {
log.error(">>>>>>>>>> 【ERROR】数据库连接失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据库连接失败,请稍后重试");
}
return tableNames;
}
/**
* 获取表名缓存
*
* @return 表名缓存
*/
public static Set<String> getTableNameCache() {
return TABLE_NAME_CACHE;
}
/**
* 动态更新配置 actualDataNodes
*/
public static void actualDataNodesRefresh() {
try {
// 获取数据分片节点
Set<String> tableNameCache = ShardingAlgorithmTool.getTableNameCache();
ShardingDataSource dataSource = (ShardingDataSource) SpringUtil.getBean("dataSource", DataSource.class);
for (int i=0;i<LOGIC_TABLE_NAME.size();i++){
TableRule tableRule = dataSource.getShardingContext().getShardingRule().getTableRule(LOGIC_TABLE_NAME.get(i));
List<DataNode> dataNodes = tableRule.getActualDataNodes();
String dataSourceName = dataNodes.get(0).getDataSourceName();
List<DataNode> newDataNodes = tableNameCache.stream().map(tableName -> new DataNode(dataSourceName + "." + tableName)).collect(Collectors.toList());
// 更新actualDataNodes
Field actualDataNodesField = TableRule.class.getDeclaredField("actualDataNodes");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(actualDataNodesField, actualDataNodesField.getModifiers() & ~Modifier.FINAL);
actualDataNodesField.setAccessible(true);
actualDataNodesField.set(tableRule, newDataNodes);
}
} catch (Exception e) {
log.error("初始化 动态表单失败,原因:{}", e.getMessage(), e);
}
}
/**
* 创建分表
*
* @param logicTableName 逻辑表名,例:order
* @param resultTableName 真实表名,例:order_20240808
* @return 创建结果(true创建成功,false未创建)
*/
public static boolean createShardingTable(String logicTableName, String resultTableName) {
synchronized (logicTableName.intern()) {
// 缓存中有此表 返回
if (TABLE_NAME_CACHE.contains(resultTableName)) {
return false;
}
// 缓存中无此表,则建表并添加缓存
executeSql(Collections.singletonList("CREATE TABLE `" + resultTableName + "` LIKE `" + logicTableName + "`;"));
// 缓存重载
tableNameCacheReload();
}
return true;
}
/**
* 执行SQL
*
* @param sqlList SQL集合
*/
private static void executeSql(List<String> sqlList) {
if (StringUtils.isEmpty(DATASOURCE_URL) || StringUtils.isEmpty(DATASOURCE_USERNAME) || StringUtils.isEmpty(DATASOURCE_PASSWORD)) {
log.error(">>>>>>>>>> 【ERROR】数据库连接配置有误,请稍后重试,URL:{}, username:{}, password:{}", DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
throw new IllegalArgumentException("数据库连接配置有误,请稍后重试");
}
try (Connection conn = DriverManager.getConnection(DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD)) {
try (Statement st = conn.createStatement()) {
conn.setAutoCommit(false);
for (String sql : sqlList) {
st.execute(sql);
}
} catch (Exception e) {
conn.rollback();
log.error(">>>>>>>>>> 【ERROR】数据表创建执行失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据表创建执行失败,请稍后重试");
}
} catch (SQLException e) {
log.error(">>>>>>>>>> 【ERROR】数据库连接失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据库连接失败,请稍后重试");
}
}
}
项目启动后读取分表进行缓存
package com.example.demo.test;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* <p> @Title ShardingTablesLoadRunner
* <p> @Description 项目启动后,读取已有分表,进行缓存
*/
@Order(value = 1) // 数字越小,越先执行
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// 读取已有分表,进行缓存
ShardingAlgorithmTool.tableNameCacheReload();
}
}
参考ShardingJDBC分表使用-CSDN博客
忘了是不是这个了
其中的分表规则不能满足我的需求,所以做了改动然后发了出来 ,
2687

被折叠的 条评论
为什么被折叠?



