1、引入Maven 依赖
org.apache.shardingsphere
shardingsphere-jdbc-core-spring-boot-starter
5.0.0
com.alibaba
druid
1.2.8
org.apache.tomcat
tomcat-dbcp
10.0.16
2、yml 配置文件
spring:
sharding-sphere:
datasource:
names: master
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:5432/production_dev?serverTimezone=UTC&characterEncoding=utf-8&stringtype=unspecified
username: postgres
password: 123456
rules:
sharding:
sharding-algorithms:
month-sharding-algorithm:
props:
strategy: standard
algorithmClassName: com.base.shading.CreateTimeShardingAlgorithm
type: CLASS_BASED
tables:
work_procedure:
actual-data-nodes: master.work_procedure_${2023..2100}_0${1..9},master.work_procedure_${2023..2100}_${10..12}
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: month-sharding-algorithm
work_result:
actual-data-nodes: master.work_result_${2023..2100}_0${1..9},master.work_result_${2023..2100}_${10..12}
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: month-sharding-algorithm
bindingTables:
- work_procedure, work_result
props:
sql-show: true
3、分片算法类 CreateTimeShardingAlgorithm.java
package com.base.shading;
import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class CreateTimeShardingAlgorithm implements StandardShardingAlgorithm {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 精准分片
* @param collection 对应分片库中所有分片表的集合
* @param preciseShardingValue 分片键值,其中 logicTableName 为逻辑表,columnName 分片键,value 为从 SQL 中解析出来的分片键的值
* @return 表名
*/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
Object value = preciseShardingValue.getValue();
String tableSuffix = null;
if(value instanceof Date){
//Date columnValue = (Date) value;
//tableSuffix = columnValue.toInstant().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy_MM"));
LocalDate localDate = ((Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
tableSuffix = localDate.format(DateTimeFormatter.ofPattern("yyyy_MM"));
}else{
String column = (String)value;
tableSuffix = LocalDateTime.parse(column, formatter).format(DateTimeFormatter.ofPattern("yyyy_MM"));
}
String logicTableName = preciseShardingValue.getLogicTableName();
String actualTableName = logicTableName.concat("_").concat(tableSuffix);
return ShardingAlgorithmTool.shardingTablesCheckAndCreatAndReturn(logicTableName, actualTableName);
}
/**
* 范围分片
* @param collection 对应分片库中所有分片表的集合
* @param rangeShardingValue 分片范围
* @return 表名集合
*/
@Override
public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) {
// 逻辑表名
String logicTableName = rangeShardingValue.getLogicTableName();
// 范围参数
Range valueRange = rangeShardingValue.getValueRange();
//起始时间 结束时间
LocalDateTime start = null;
LocalDateTime end = null;
Object lowerEndpoint = (Object)valueRange.lowerEndpoint();
Object upperEndpoint = (Object)valueRange.upperEndpoint();
if(lowerEndpoint instanceof String){
String lower = (String) lowerEndpoint;
String upper = (String) upperEndpoint;
start = LocalDateTime.parse(lower,formatter);
end = LocalDateTime.parse(upper,formatter);
}else{
start = valueRange.lowerEndpoint().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
end = valueRange.upperEndpoint().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}
if(end.isAfter(LocalDateTime.now())){
end = LocalDateTime.now();
}
// 查询范围的表
Set queryRangeTables = extracted(logicTableName, start, end);
// 数据库中的表
HashSet tableNameSet = ShardingAlgorithmTool.cacheTableNames();
//返回的表
ArrayList tables = new ArrayList<>(collection);
tables.retainAll(queryRangeTables);
tables.retainAll(tableNameSet);
return tables;
}
@Override
public String getType() {
return null;
}
@Override
public Properties getProps() {
return null;
}
@Override
public void init() {
}
/**
* 根据范围计算表名
*
* @param logicTableName 逻辑表名
* @param lowerEndpoint 范围起点
* @param upperEndpoint 范围终端
* @return 物理表名集合
*/
private Set extracted(String logicTableName, LocalDateTime lowerEndpoint, LocalDateTime upperEndpoint) {
Set rangeTable = new HashSet<>();
while (lowerEndpoint.isBefore(upperEndpoint)) {
String str = getTableNameByDate(lowerEndpoint, logicTableName);
rangeTable.add(str);
lowerEndpoint = lowerEndpoint.plusMonths(1);
}
// 获取物理表名
String tableName = getTableNameByDate(upperEndpoint, logicTableName);
rangeTable.add(tableName);
return rangeTable;
}
/**
* 根据日期获取表名
* @param dateTime 日期
* @param logicTableName 逻辑表名
* @return 物理表名
*/
private String getTableNameByDate(LocalDateTime dateTime, String logicTableName) {
String tableSuffix = dateTime.format(DateTimeFormatter.ofPattern("yyyy_MM"));
return logicTableName.concat("_").concat(tableSuffix);
}
}
4、 分片工具类 ShardingAlgorithmTool.java
package com.base.shading;
import com.base.utils.ApplicationContextUtil;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@Slf4j
public class ShardingAlgorithmTool {
private static final HashSet tableNameCache = new HashSet<>();
/**
* 获取所有表名
* @return 表名集合
*/
public static List getAllTableNameBySchema() {
List tableNames = new ArrayList<>();
String sql = "SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename ~ 'work_procedure_\\d{4}_\\d{2}$' OR tablename ~ 'work_result_\\d{4}_\\d{2}$';";
DataSource dataSource = ApplicationContextUtil.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection()) {
Statement statement = connection.createStatement();
try (ResultSet rs = statement.executeQuery(sql)) {
while (rs.next()) {
String actualTableName = rs.getString(1);
tableNames.add(actualTableName);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return tableNames;
}
/**
* 判断 分表获取的表名是否存在 不存在则自动建表
*
* @param logicTableName 逻辑表名(表头)
* @param actualTableName 真实表名
* @return 确认存在于数据库中的真实表名
*/
public static String shardingTablesCheckAndCreatAndReturn(String logicTableName, String actualTableName) {
synchronized (logicTableName.intern()) {
// 缓存中有此表 返回
if (tableNameCache.contains(actualTableName)) {
return actualTableName;
}
String sql="CREATE TABLE "+ actualTableName +" (LIKE "+ logicTableName +" INCLUDING CONSTRAINTS);";
// 缓存中无此表,则建表并添加缓存
DataSource dataSource = ApplicationContextUtil.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate(sql);
}catch (SQLException e){
throw new RuntimeException(e);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
// 缓存重载
tableNameCacheReload();
//添加缓存
//tableNameCache.add(resultTableName);
}
return actualTableName;
}
/**
* 缓存重载方法
*/
public static void tableNameCacheReload() {
// 读取数据库中所有表名
List tableNameList = getAllTableNameBySchema();
// 删除旧的缓存(如果存在)
ShardingAlgorithmTool.tableNameCache.clear();
// 写入新的缓存
ShardingAlgorithmTool.tableNameCache.addAll(tableNameList);
}
/**
* 获取缓存中的表名
* @return
*/
public static HashSet cacheTableNames() {
return tableNameCache;
}
}
5. 初始化缓存类 ShardingTablesLoadRunner.java
package com.base.shading;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(value = 1) // 数字越小,越先执行
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// 读取已有分表,进行缓存
ShardingAlgorithmTool.tableNameCacheReload();
}
}
6. Spring工具类 ApplicationContextUtil .java
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;
@Component
@Slf4j
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(ApplicationContextUtil.applicationContext == null) {
ApplicationContextUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return ApplicationContextUtil.applicationContext;
}
public static T getBean(Class clazz) {
return applicationContext.getBean(clazz);
}
public static T getBean(String name, Class clazz) {
return applicationContext.getBean(name, clazz);
}
public static String getProperty(String key) {
return applicationContext.getBean(Environment.class).getProperty(key);
}
public static Object getBean(String name){
return applicationContext.getBean(name);
}
}
7、注意
(1)SQL中where后面的查询条件包含分表规则中配置的sharding-column字段才会进入,定义的algorithmClassName分片算法类中。
(2)自定义SQL中表名和sharding-column字段最好不要用双引号包括。
(3)IDEA中yml配置文件中有些属性显示红色,也不要紧可以识别。
(4)修改查询中没有出现sharding-column字段的查询。