ShardingSphere简介

ShardingSphere 由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的基于数据库作为存储节点的增量功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

关系型数据库当今依然占有巨大市场份额,是企业核心系统的基石,未来也难于撼动,我们更加注重在原有基础上提供增量,而非颠覆。

ShardingSphere-JDBC

定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;

  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;

  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。

总结 兼容性好 , 目前主流的ORM框架均能够支持、而且只需要在pom文件中引入依赖即可、使用起来非常方便。

查看更多关于分库分表、读写分离:https://mp.weixin.qq.com/s/aFXZ8rT9g4oj3ZnioC2vkg

添加依赖

<!-- 关系型数据库中间件sharding -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.1</version>
</dependency>

配置数据源

@Configuration
public class DruidConfig{ @Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
}
/**
* 数据源
*/
public enum DataSourceType
{
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE,
/**
* 分表
*/
SHARDING
}
/**
* 在Mapper类中配置分表数据源
*/
@DataSource(DataSourceType.SHARDING)
public interface TestMapper {
// 针对分表的所有增删改查操作,都要重新写对应的SQL语句,否则直接mybatis接口不会生效
}

添加yml配置

spring:
main:
# 一个实体类对应多张表,必须设置这个
allow-bean-definition-overriding: true
shardingsphere:
props:
sql:
# 打印SQL
show: false
datasource:
names: master
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/times_tool?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
sharding:
tables:
ts_dir_oss:
# 配置表规则
actual-data-nodes: master.ts_dir_oss_$->{2013..2023}_$->{(1..12).collect{t ->t.toString().padLeft(2,'0')}}_$->{(1..2).collect{t ->t.toString().padLeft(2,'0')}}
# 配置主键id生成策略,指定雪花算法
key-generator:
column: id
type: SNOWFLAKE
table-strategy:
standard:
# 分片字段
sharding-column: point_time
# 标准策略 + 精确分片算法 SQL就是(=或in)
precise-algorithm-class-name: com.cn.framework.datasource.PointTimePreciseShardingAlgorithm
# 标准策略 + 范围分片算法 主要是(between A and B)
range-algorithm-class-name: com.cn.framework.datasource.PointTimeRangeShardingAlgorithm
# 复合分片策略 提供对SQL语句中的(=,in,beteewn A and B)的分片操作支持
# complex:
# sharding-columns: point_time
# algorithm-class-name: com.cn.framework.datasource.ComplexShardingAlgorithm

代码实现:一张表存储半月数据

复合分片策略

/**
* 复合分片策略 提供对SQL语句中的(=,in,beteewn A and B)的分片操作支持.
*/
@Slf4j
public class ComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm { @Override
public Collection<String> doSharding(Collection collection, ComplexKeysShardingValue complexKeysShardingValue) {
Set<String> tables = new HashSet<>();
Map<String, Range<String>> rangeMap = complexKeysShardingValue.getColumnNameAndRangeValuesMap();
if(CollUtil.isNotEmpty(rangeMap)){
for(String s : rangeMap.keySet()){
Range<String> valueRange = rangeMap.get(s);
if(valueRange!=null){
Date begin = DateUtil.parse(valueRange.lowerEndpoint(), DateUtils.YYYY_MM);
Date end = DateUtil.parse(valueRange.upperEndpoint(),DateUtils.YYYY_MM);
log.info("lowerSuffix:{},upperSuffix:{}",begin,end);
for(Object tab:collection){
String tableName = (String)tab;
String dateStr = StrUtil.subAfter(tableName, "ts_dir_oss_", true).replace("_", "-");
Date date = DateUtil.parse(dateStr,DateUtils.YYYY_MM);
if((date.after(begin) || date.equals(begin)) && (date.before(end) || date.equals(end) )){
tables.add(tableName);
}
}
}
}
}
Map<String, List<String>> map = complexKeysShardingValue.getColumnNameAndShardingValuesMap();
if(CollUtil.isNotEmpty(map)){
for(String s : map.keySet()){
List<String> list = map.get(s);
if(list.size()>0){
String v = list.get(0);
Date date = DateUtil.parseDate(v);
String suffix = getSuffixByDate(date);
for(Object tab: collection){
String tableName = (String)tab;
if(tableName.endsWith(suffix)){
tables.add(tableName);
}
}
}
}
}
log.info("match tableNames:{}", tables.toString());
return tables;
} public String getSuffixByDate(Date date){
String dateStr = DateUtil.format(date,"yyyy_MM");
int day = DateUtil.date(date).dayOfMonth();
if(day<=15) {
return dateStr + "_01";
}else{
return dateStr + "_02";
}
}
}

标准策略+精确分片算法

/**
* 标准策略 + 精确分片算法 SQL就是(=或in)
*/
@Slf4j
public class PointTimePreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> { @Override
public String doSharding(Collection<String> availableTargetName, PreciseShardingValue<String> preciseShardingValue) {
Date date = DateUtil.parseDate(preciseShardingValue.getValue());
String suffix = getSuffixByDate(date);
for(String tabName: availableTargetName){
if(tabName.endsWith(suffix)){
log.info("match tableName:{}", tabName);
return tabName;
}
}
throw new IllegalArgumentException("未找到匹配的数据表");
}
public String getSuffixByDate(Date date){
String dateStr = DateUtil.format(date,"yyyy_MM");
int day = DateUtil.date(date).dayOfMonth();
if(day<=15) {
return dateStr + "_01";
}else{
return dateStr + "_02";
}
}
}

标准策略+范围分片算法

/**
* 标准策略 + 范围分片算法 主要是(between A and B)
*/
@Slf4j
public class PointTimeRangeShardingAlgorithm implements RangeShardingAlgorithm<String> { @Override
public Collection<String> doSharding(Collection<String> availableTargetName, RangeShardingValue<String> rangeShardingValue) {
Range<String> valueRange = rangeShardingValue.getValueRange();
Date begin = DateUtil.parse(valueRange.lowerEndpoint(), DateUtils.YYYY_MM);
Date end = DateUtil.parse(valueRange.upperEndpoint(),DateUtils.YYYY_MM);
log.info("lowerSuffix:{},upperSuffix:{}",begin,end);
Set<String> tables = new HashSet<>();
for(String tableName:availableTargetName){
String dateStr = StrUtil.subAfter(tableName, "ts_dir_oss_", true).replace("_", "-");
Date date = DateUtil.parse(dateStr,DateUtils.YYYY_MM);
if((date.after(begin) || date.equals(begin)) && (date.before(end) || date.equals(end) )){
tables.add(tableName);
}
}
log.info("match tableNames:{}", tables.toString());
return tables;
}
}

动态新增数据表脚本

CREATE TABLE IF NOT EXISTS ts_dir_oss_${tableSuffix} (
`id` BIGINT(20) AUTO_INCREMENT NOT NULL,
`nodes_id` bigint(20) DEFAULT NULL COMMENT '目录节点id',
`point_id` varchar(48) DEFAULT NULL COMMENT '测点名称',
`point_time` varchar(18) DEFAULT NULL COMMENT '测点时间',
`path` varchar(225) DEFAULT NULL COMMENT '地址',
`size` bigint(11) DEFAULT NULL COMMENT '文件大小',
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_point_time` (`point_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

动态更新分片策略

@Component
public class ShardingTableRuleActualTablesRefresh { private Logger logger = LoggerFactory.getLogger(ShardingTableRuleActualTablesRefresh.class); @Resource(name = "shardingDataSource")
private DataSource dataSource; @PostConstruct
public void initData(){
try {
List<String> sdList = new ArrayList<>();
// 启动项目时初始化分片规则,查出分片配置表信息
actualTablesRefresh(sdList);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 动态刷新变量表的分片策略, 核心通过反射更新tableRule
* @param idList
*/
public void actualTablesRefresh(List<String> sdList) {
try {
logger.info("-----开始刷新表节点-----");
if(CollUtil.isEmpty(idList)){
logger.error("传入idList参数为空");
return;
}
ShardingDataSource shardingDataSource = (ShardingDataSource)dataSource;
//运行时获取分片规则
ShardingRule rule = shardingDataSource.getRuntimeContext().getRule(); //获取分表策略集合
Collection<TableRule> tableRules = rule.getTableRules(); for (TableRule tableRule : tableRules) {
//获取真实节点
List<DataNode> actualDataNodes = tableRule.getActualDataNodes(); Field actualDataNodesField = TableRule.class.getDeclaredField("actualDataNodes");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(actualDataNodesField, actualDataNodesField.getModifiers() & ~Modifier.FINAL); //数据源名
String dataSourceName = actualDataNodes.get(0).getDataSourceName();
//逻辑表名
String logicTableName = tableRule.getLogicTable();
//根据真实业务,新增节点的逻辑
for (String sd : sdList) {
actualDataNodes.add(new DataNode(dataSourceName+"."+logicTableName+"_" + sd));
}
actualDataNodesField.setAccessible(true);
actualDataNodesField.set(tableRule, actualDataNodes); Set<String> actualTables = Sets.newHashSet();
Map<DataNode, Integer> dataNodeIntegerMap = Maps.newHashMap();
//更新actualTables、dataNodeIntegerMap
AtomicInteger a = new AtomicInteger(0);
actualDataNodes.forEach((dataNode -> {
actualTables.add(dataNode.getTableName());
if (a.intValue() == 0){
a.incrementAndGet();
dataNodeIntegerMap.put(dataNode, 0);
}else {
dataNodeIntegerMap.put(dataNode, a.intValue());
a.incrementAndGet();
}
})); //动态刷新:actualTables
Field actualTablesField = TableRule.class.getDeclaredField("actualTables");
actualTablesField.setAccessible(true);
actualTablesField.set(tableRule, actualTables);
//动态刷新:dataNodeIndexMap
Field dataNodeIndexMapField = TableRule.class.getDeclaredField("dataNodeIndexMap");
dataNodeIndexMapField.setAccessible(true);
dataNodeIndexMapField.set(tableRule, dataNodeIntegerMap);
//动态刷新:datasourceToTablesMap
Map<String, Collection<String>> datasourceToTablesMap = Maps.newHashMap();
datasourceToTablesMap.put(dataSourceName, actualTables);
Field datasourceToTablesMapField = TableRule.class.getDeclaredField("datasourceToTablesMap");
datasourceToTablesMapField.setAccessible(true);
datasourceToTablesMapField.set(tableRule, datasourceToTablesMap);
logger.info("-----------------end----------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

注意事项

  • sharding不支持部分复杂的聚合查询,如果要有统计数据需求,可以用一个统计表实时或定时实现数据同步。
  • 数据表需要根据切片规则提前创建好,否则程序会报错提示找不到指定表。

SpringBoot集成ShardingSphere分表中间件的更多相关文章

  1. 一文快速入门分库分表中间件 Sharding-JDBC (必修课)

    书接上文 <一文快速入门分库分表(必修课)>,这篇拖了好长的时间,本来计划在一周前就该写完的,结果家庭内部突然人事调整,领导层进行权利交接,随之宣布我正式当爹,紧接着家庭地位滑落至第三名, ...

  2. 支付宝分库分表中间件--zdal简介

    中间件, 如果仅仅作为一名用户的话, 主要关注一下如何使用即可, 大多数情况下也就是配置. 下面简单的介绍一下支付宝的分库分表中间件--->zdal在web项目中的配置. 1, 在网上查阅相关资 ...

  3. 当当开源sharding-jdbc,轻量级数据库分库分表中间件

    近期,当当开源了数据库分库分表中间件sharding-jdbc. Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据 ...

  4. 解读分库分表中间件Sharding-JDBC

    [编者按]数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据 ...

  5. 分库分表中间件Sharding-JDBC

    数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据和高并发对 ...

  6. 分库分表中间件sharding-jdbc的使用

    数据分片产生的背景,可以查看https://shardingsphere.apache.org/document/current/cn/features/sharding/,包括了垂直拆分和水平拆分的 ...

  7. springboot+jpa分库分表项目实例

    分库分表场景 关系型数据库本身比较容易成为系统瓶颈,单机存储容量.连接数.处理能力都有限.当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库.优化索引,做很多操作时性能仍下降严 ...

  8. Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离

    结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...

  9. Mysql系列五:数据库分库分表中间件mycat的安装和mycat配置详解

    一.mycat的安装 环境准备:准备一台虚拟机192.168.152.128 1. 下载mycat cd /softwarewget http:-linux.tar.gz 2. 解压mycat tar ...

  10. Mycat 数据库分库分表中间件

    http://www.mycat.io/ Mycat 国内最活跃的.性能最好的开源数据库中间件! 我们致力于开发高性能的开源中间件而努力! 实体书Mycat权威指南 »开源投票支持Mycat下载 »s ...

随机推荐

  1. 显示器AVG、DVI、HDMI、DisplayPort、Type-C、雷电接口

    在近十年的发展,显示设备的接口发生了巨大的改变.以前使用比较多的是蓝色VGA接口,接著出现了白色的DVI接口,当遇到不同接口时,还得买转接头进行转接.后来,又有了HDMI等接口,现在则出现DP和USB ...

  2. mod操作符效率高吗?

    编程语言中mod取余操作符%的效率不是很高,比如M = N % 10,它花费得时间本机测试是1ms,而如果使用M = N - N / 10 * 10,则只需要0.1ms. 所以平时变成得时候,可以尽量 ...

  3. 在tomcat上安装PFX格式证书部署https

    您可以在Tomcat服务器安装已签发的SSL证书,实现通过HTTPS安全访问Web服务.本文介绍如何在Tomcat服务器安装PFX格式的SSL证书. 步骤一:在阿里云的域名管理后台,下载SSL证书 登 ...

  4. Linux下tail -f,tail -F,tailf的区别

    在Linux中,tail -f ,tail -F,tailf都是用来查看滚动日志的好方法,但是三者之间却有着细微的不同: ​ tail -f 等同于--follow=descriptor,动态显示数据 ...

  5. CeiT:商汤提出结合CNN优势的高效ViT模型 | 2021 arxiv

    论文提出CeiT混合网络,结合了CNN在提取低维特征方面的局部性优势以及Transformer在建立长距离依赖关系方面的优势.CeiT在ImageNet和各种下游任务中达到了SOTA,收敛速度更快,而 ...

  6. 跨域问题服务端解决办法 Request header field Authorization is not allowed by Access-Control-Allow-Headers

    跨域问题服务端解决办法 一般在入口文件加 header('Access-Control-Allow-Origin:*');// 响应类型header('Access-Control-Allow-Met ...

  7. .NetCore 3.1 教程之 EFCore连接Mysql DBFirst模式 从数据库生成实体

    一:创建EF的类库,同时将此项目设置为启动项(为Scaffold-DbContext -tables指令使用),同时安装2个包   ①Microsoft.EntityFrameworkCore.Too ...

  8. Flutter(五):Flutter 加入现有App的方案选择(Flutter_Boost)

    一.介绍 用 Flutter 一次性重写整个已有的应用是不切实际的.对于这些情况,Flutter 可以作为一个库或模块,集成进现有的应用当中.模块引入到您的 Android 或 iOS 应用(当前支持 ...

  9. Qt(python) + 百度语音合成 实现demo

    python实现 安装api sudo pip3 install baidu-aip 安装音频处理模块pydub sudo pip3 install pydub from aip import Aip ...

  10. scala怎么退出

    scala怎么退出 scala> :help //查看帮助 All commands can be abbreviated, e.g., :he instead of :help. :edit ...