4升到5过后还是解决了许多问题,4版本的跨库和子查询问题都可以了,性能也提高了 实现自动创建表,动态获取节点表,配置如下 建表语句可以使用下面这句,解决很多麻烦问题 CREATE TABLE newName LIKE oldName
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>10.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
读写分离需要配置数据库有主从同步
spring:
shardingsphere:
enabled: true
props:
sql-show: true
datasource:
names: master,slave
master:
type: ${spring.datasource.type}
driver-class-name: ${spring.datasource.hikari.driver-class-name}
url: ${spring.datasource.hikari.jdbc-url}
username: ${spring.datasource.hikari.username}
password: ${spring.datasource.hikari.password}
slave:
type: ${spring.datasource.type}
driver-class-name: ${spring.datasource.hikari.driver-class-name}
jdbc-url: jdbc:mysql://127.0.0.1:3308/tecloman_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: ${spring.datasource.hikari.username}
password: 123456
rules:
sharding:
tables:
hss_history:
actual-data-nodes: master.hss_history,master.hss_history_202$->{201..212}
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: hss-history-inline
sharding-algorithms:
hss-history-inline:
type: CLASS_BASED
props:
strategy: standard
algorithmClassName: mqtt.server.sharding.DateShardingAlgorithm
binding-tables: hss_history
default-key-generate-strategy:
column: id
key-generator-name: id-key
key-generators:
id-key:
type: SNOWFLAKE
readwrite-splitting:
data-sources:
master:
type: STATIC
props:
write-data-source-name: master
read-data-source-names: slave
load-balancer-name: round
load-balancers:
round:
type: ROUND_ROBIN
jackson:
time-zone: UTC
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
jdbc-url: jdbc:mysql://127.0.0.1:3307/tecloman_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
分片策略实现类 和4版本不一样,5版本更新和查询合并在一起了 实现 StandardShardingAlgorithm
package mqtt.server.sharding;
import hss.server.utils.DateUtils;
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 org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@Component
public class DateShardingAlgorithm implements StandardShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
for (String s : collection) {
System.out.println("节点配置表名为: "+s);
}
List<String> tableNames = ShardingAlgorithmTool.getAllTableNameBySchema();
for (String s : tableNames) {
System.out.println("数据库实时表名: "+s);
}
HashSet<String> tableNameCache = ShardingAlgorithmTool.cacheTableNames();
for (String s : tableNameCache) {
System.out.println("缓存中的表名: "+s);
}
return tableNameCache;
}
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
StringBuilder resultTableName = new StringBuilder();
String logicTableName = preciseShardingValue.getLogicTableName();
resultTableName.append(logicTableName)
.append("_").append(DateUtils.format(new Date(preciseShardingValue.getValue() * 1000), DateUtils.YEAR_MONTH_NUMBER));
System.out.println("插入表名为:" + resultTableName);
return ShardingAlgorithmTool.shardingTablesCheckAndCreatAndReturn(logicTableName, resultTableName.toString());
}
@Override
public void init() {
}
@Override
public String getType() {
return null;
}
}
缓存工具类,自动创建表,放入缓存中,
package mqtt.server.sharding;
import hss.server.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@Slf4j
public class ShardingAlgorithmTool {
private static final HashSet<String> tableNameCache = new HashSet<>();
public static String shardingTablesCheckAndCreatAndReturn(String logicTableName, String resultTableName) {
synchronized (logicTableName.intern()) {
if (tableNameCache.contains(resultTableName)) {
return resultTableName;
}
List<String> sqlList = selectTableCreateSql(logicTableName);
for (int i = 0; i < sqlList.size(); i++) {
sqlList.set(i, sqlList.get(i).replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS").replace(logicTableName, resultTableName));
}
executeSql(sqlList);
tableNameCache.add(resultTableName);
}
return resultTableName;
}
public static void tableNameCacheReload() {
List<String> tableNameList = getAllTableNameBySchema();
ShardingAlgorithmTool.tableNameCache.clear();
ShardingAlgorithmTool.tableNameCache.addAll(tableNameList);
}
private static void executeSql(List<String> sqlList) {
Environment env = SpringUtil.getApplicationContext().getEnvironment();
try (Connection conn = DriverManager.getConnection(Objects.requireNonNull(env.getProperty("spring.datasource.hikari.jdbc-url")), env.getProperty("spring.datasource.hikari.username"), env.getProperty("spring.datasource.hikari.password"))) {
try (Statement st = conn.createStatement()) {
conn.setAutoCommit(false);
for (String sql : sqlList) {
st.execute(sql);
}
conn.commit();
} catch (Exception ex) {
conn.rollback();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static List<String> selectTableCreateSql(String tableName) {
List<String> res = new ArrayList<>();
if (tableName.equals("hss_history")) {
res.add("CREATE TABLE `hss_history` (\n" +
" `id` bigint unsigned NOT NULL,\n" +
" `type_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '设备类型id',\n" +
" `equipment_id` bigint unsigned NOT NULL COMMENT '设备id',\n" +
" `data` json DEFAULT NULL COMMENT '原始数据',\n" +
" `parse_data` json DEFAULT NULL COMMENT '解析数据',\n" +
" `parse_time` bigint NOT NULL DEFAULT '0' COMMENT '解析时间',\n" +
" `create_time` bigint NOT NULL DEFAULT '0',\n" +
" PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='设备历史数据'");
res.add("ALTER TABLE `hss_history` ADD INDEX n1 ( `create_time`, `equipment_id` ) USING BTREE");
res.add("ALTER TABLE `hss_history` ADD INDEX n2 ( `equipment_id` ) USING BTREE");
res.add("ALTER TABLE `hss_history` ADD INDEX n3 ( `parse_time` ) USING BTREE");
}
return res;
}
public static List<String> getAllTableNameBySchema() {
List<String> res = new ArrayList<>();
Environment env = SpringUtil.getApplicationContext().getEnvironment();
try (Connection connection = DriverManager.getConnection(env.getProperty("spring.datasource.hikari.jdbc-url"), env.getProperty("spring.datasource.hikari.username"), env.getProperty("spring.datasource.hikari.password"));
Statement st = connection.createStatement()) {
try (ResultSet rs = st.executeQuery("show TABLES like 'hss_history%'")) {
while (rs.next()) {
res.add(rs.getString(1));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
public static HashSet<String> cacheTableNames() {
return tableNameCache;
}
}
项目启动就加载缓存 更新和查询数据直接从缓存中读取表名
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Order(value = 1)
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {
@Override
public void run(String... args) {
ShardingAlgorithmTool.tableNameCacheReload();
}
}
时间工具类
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateUtils {
public final static String DATE_PATTERN = "yyyy-MM-dd";
public static final String YEAR_MONTH_NUMBER = "yyyyMM";
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static String format(Date date) {
return format(date, DATE_PATTERN);
}
public static String format(Date date, String pattern) {
if (date != null) {
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
return null;
}
public static Date stringToDate(String strDate, String pattern) {
if (StringUtils.isBlank(strDate)) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(strDate, new ParsePosition(0));
}
public static String format(Integer time, String pattern) {
Date date = new Date((long) time * 1000);
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
public static Date[] getWeekStartAndEnd(int week) {
DateTime dateTime = new DateTime();
LocalDate date = new LocalDate(dateTime.plusWeeks(week));
date = date.dayOfWeek().withMinimumValue();
Date beginDate = date.toDate();
Date endDate = date.plusDays(6).toDate();
return new Date[]{beginDate, endDate};
}
public static Date addDateSeconds(Date date, int seconds) {
DateTime dateTime = new DateTime(date);
return dateTime.plusSeconds(seconds).toDate();
}
public static Date addDateMinutes(Date date, int minutes) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMinutes(minutes).toDate();
}
public static Date addDateHours(Date date, int hours) {
DateTime dateTime = new DateTime(date);
return dateTime.plusHours(hours).toDate();
}
public static Date addDateDays(Date date, int days) {
DateTime dateTime = new DateTime(date);
return dateTime.plusDays(days).toDate();
}
public static Date addDateWeeks(Date date, int weeks) {
DateTime dateTime = new DateTime(date);
return dateTime.plusWeeks(weeks).toDate();
}
public static Date addDateMonths(Date date, int months) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMonths(months).toDate();
}
public static Date addDateYears(Date date, int years) {
DateTime dateTime = new DateTime(date);
return dateTime.plusYears(years).toDate();
}
public static boolean isDate(String date, String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
sdf.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
public static Date todayStart() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
public static Date todayEnd() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar.getTime();
}
}
自动刷新节点表,也可以采用别的方式进行, sharding5.1还是有许多坑,许多依赖冲突。 顺便讲一下,我分片键是Long,但实际上是Date 转为Long的,前端传入后端会自动转换成Long时间戳存入数据库,查询将时间戳转成date返回前端
@TableField(typeHandler = DateLongTypeHandler.class)
private Date createTime;
Date和Long互转 handler
import org.apache.ibatis.type.*;
import org.springframework.stereotype.Component;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
@Component
@MappedTypes({Date.class})
@MappedJdbcTypes({JdbcType.BIGINT})
public class DateLongTypeHandler extends BaseTypeHandler<Date> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException(
"JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException(
"Error setting null for parameter #"
+ i
+ " with JdbcType "
+ jdbcType
+ " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
ps.setLong(i, parameter.getTime() / 1000);
}
}
@Override
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
long res = rs.getLong(columnName);
if (res == 0) {
return null;
}
long time = res * 1000;
return new Date(time);
}
@Override
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
long res = rs.getLong(columnIndex);
if (res == 0) {
return null;
}
long time = res * 1000;
return new Date(time);
}
@Override
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
long res = cs.getLong(columnIndex);
if (res == 0) {
return null;
}
long time = res * 1000;
return new Date(time);
}
}
|