【ShardingSphere技术专题】「ShardingJDBC」SpringBoot之整合ShardingJDBC实现分库分表(JavaConfig方式)

简介: 【ShardingSphere技术专题】「ShardingJDBC」SpringBoot之整合ShardingJDBC实现分库分表(JavaConfig方式)

前提介绍


ShardingSphere介绍


ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。


image.png


shardingJDBC使用的范围


  • 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。


详细一点的介绍直接看官网:shardingsphere.apache.org/document/cu…



SQL语句相关


  • 逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为2张表,分别是t_order_0到t_order_1,他们的逻辑表名为t_order。
  • 真实表:在分片的数据库中真实存在的物理表。例:示例中的t_order_0到t_order_1
  • 数据节点:数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0;ds_0.t_order_1;
  • 绑定表:指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
  • 广播表:指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景。



数据分片相关


  • 分片键:用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。


SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。


  • 分片算法:通过分片算法将数据分片,支持通过=、>=、<=、>、<、BETWEEN和IN分片,分片算法需要应用方开发者自行实现,可实现的灵活度非常高。




目前提供4种分片算法


  • 精确分片算法:对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
  • 范围分片算法:对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合StandardShardingStrategy使用。
  • 复合分片算法:对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
  • Hint分片算法:对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。


分片策略:包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。




目前提供5种分片策略



  • 标准分片策略:对应StandardShardingStrategy,提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。
  • StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
  • PreciseShardingAlgorithm是必选的,用于=和IN的分片
  • RangeShardingAlgorithm是可选的,用于BETWEEN AND, >, <, >=, <=分片,不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
  • 复合分片策略:对应ComplexShardingStrategy。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持


  • ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装。
  • 而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。



  • 行表达式分片策略:对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。


  • 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
  • Hint分片策略:对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
  • 不分片策略:对应NoneShardingStrategy。




配置相关


分片规则:分片规则配置的总入口。包含数据源配置、表配置、绑定表配置以及读写分离配置等。


  • 数据源配置:真实数据源列表。
  • 表配置:逻辑表名称、数据节点与分表规则的配置
  • 数据节点配置:用于配置逻辑表与真实表的映射关系。
  • 分片策略配置:
  • 数据源分片策略:对应于DatabaseShardingStrategy。用于配置数据被分配的目标数据源。
  • 表分片策略:对应于TableShardingStrategy。用于配置数据被分配的目标表,该目标表存在与该数据的目标数据源内。故表分片策略是依赖与数据源分片策略的结果的。



  • 自增主键生成策略:通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。(雪花算法)

开发步骤


开发整合方式


方式一:基于配置文件集成,方便简单但是不够灵活

<!--主要有以下依赖,分库分表策略直接在application.properties做相关配置即可-->
  <dependency>
    <groupId>io.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>3.1.0.M1</version>
  </dependency>
  <dependency>
    <groupId>io.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-namespace</artifactId>
    <version>3.1.0.M1</version>
  </dependency>
复制代码



方式二:这里我们主要基于java config的方式来集成到springboot中,更适合学习和理解

//相关依赖
<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-jdbc-core</artifactId>
  <version>3.1.0</version>
</dependency>
<!--<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-transaction-2pc-xa</artifactId>
  <version>3.1.0</version>
</dependency>-->
<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-jdbc-orchestration</artifactId>
  <version>3.1.0</version>
</dependency>
<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-orchestration-reg-zookeeper-curator</artifactId>
  <version>3.1.0</version>
</dependency>
复制代码



定义相关配置类(DataSourceConfig => MybatisConfig => TransactionConfig)


ShardingSphereDataSourceConfig


import javax.sql.DataSource;
import java.lang.management.ManagementFactory;
import java.sql.SQLException;
import java.util.*;
/**
 * @Author zhangboqing
 * @Date 2020/4/25
 */
@Configuration
@Slf4j
public class ShardingSphereDataSourceConfig {
    @Bean("shardingDataSource")
    DataSource getShardingDataSource() throws SQLException {
        //初始化相关的分片规则配置信息控制机制
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // 设置相关的数据源
        shardingRuleConfig.setDefaultDataSourceName("ds0");
       // 设置相关的Order表的相关的规则信息配置机制
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
       // 设置相关的OrderItem表的相关的规则信息配置机制
        shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        // 配置绑定表关系
        shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
        // 广播表操作机制
        shardingRuleConfig.getBroadcastTables().add("t_config");
        // 设置相关的分片机制策略(数据源分片策略机制控制)
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "ds${user_id % 2}"));
        // 设置相关的分片策略机制,子啊inline模式下(包含了两种模式)
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(getShardingStrategyConfiguration());
        // ShardingPropertiesConstant相关配置选项
        Properties properties = new Properties();
        //是否打印SQL解析和改写日志
        properties.put("sql.show",true);
       //用于SQL执行的工作线程数量,为零则表示无限制
        propertie.setProperty("executor.size","4");
       //每个物理数据库为每次查询分配的最大连接数量
        propertie.setProperty("max.connections.size.per.query","1");
       //是否在启动时检查分表元数据一致性
        propertie.setProperty("check.table.metadata.enabled","false");
    //用户自定义属性
        Map<String, Object> configMap = new HashMap<>();
        configMap.put("effect","分库分表");
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, properties);
    }
    // 配置相关的分片策略机制
    private ShardingStrategyConfiguration getShardingStrategyConfiguration(){
        // 精确匹配
        PreciseShardingAlgorithm<Long> preciseShardingAlgorithm = new PreciseShardingAlgorithm<Long>() {
            @Override
            public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
                String prefix = shardingValue.getLogicTableName(); //逻辑表名称
                Long orderId = shardingValue.getValue(); //订单编码
                long index = orderId % 2; //订单表(分表路由索引)
                // t_order + "" + 0 = t_order0
                String tableName = prefix + "" +index;
                // 精确查询、更新之类的,可以返回不存在表,进而给前端抛出异常和警告。
                if (availableTargetNames.contains(tableName) == false) {
                    LogUtils.error(log,"PreciseSharding","orderId:{},不存在对应的数据库表{}!", orderId, tableName);
                    return availableTargetNames.iterator().next();
                }
                return tableName;
//                return availableTargetNames.iterator().next();
            }
        };
        // 范围匹配
        RangeShardingAlgorithm<Long> rangeShardingAlgorithm = new RangeShardingAlgorithm<Long>() {
            @Override
            public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
                String prefix = shardingValue.getLogicTableName();
                Collection<String> resList = new ArrayList<>();
                // 获取相关的数据值范围
                Range<Long> valueRange = shardingValue.getValueRange();
                // 如果没有上限或者下限的没有,则直接返回所有的数据表
                if (!valueRange.hasLowerBound() || !valueRange.hasUpperBound()) {
                    return availableTargetNames;
                }
               // 获取下限数据范围
                long lower = shardingValue.getValueRange().lowerEndpoint();
                BoundType lowerBoundType = shardingValue.getValueRange().lowerBoundType();
               // 获取下限数据范围
                long upper = shardingValue.getValueRange().upperEndpoint();
                BoundType upperBoundType = shardingValue.getValueRange().upperBoundType();
               // 下限数据信息值
                long startValue = lower;
                long endValue = upper;
               // 是否属于开区间(下限)
                if (lowerBoundType.equals(BoundType.OPEN)) {
                    startValue++; //缩减范围1
                }
               // 是否属于开区间(上限)
                if (upperBoundType.equals(BoundType.OPEN)) {
                    endValue--; // 缩减范围1
                }
               // 进行计算相关所需要是实体表
                for (long i = startValue; i <= endValue ; i++) {
                    long index = i % 2;
                    String res = prefix + "" +index;
                    // 精确查询、更新之类的,可以返回不存在表,进而给前端抛出异常和警告。
                    if (availableTargetNames.contains(res) == false) {
                        LogUtils.error(log,"RangeSharding","orderId:{},不存在对应的数据库表{}!", i, res);
                    }else{
                       resList.add(res);
                   }
                }
                if (resList.size() == 0) {
                    LogUtils.error(log,"RangeSharding","无法获取对应表,因此将对全表进行查询!orderId范围为:{}到{}",startValue,endValue);
                    return availableTargetNames;
                }
                return resList;
            }
        };
        // 设置相关整体的算法整合
        ShardingStrategyConfiguration strategyConf = new StandardShardingStrategyConfiguration("order_id", preciseShardingAlgorithm, rangeShardingAlgorithm);
        return strategyConf;
    }
    // 获取相关的Order订单规则表配置信息控制配置控制机制
    TableRuleConfiguration getOrderTableRuleConfiguration() {
        // 逻辑表 + 实际节点 :设置逻辑表与数据节点(数据分片的最小单位)的映射关系机制
        TableRuleConfiguration result = new TableRuleConfiguration("t_order", "ds${0..1}.t_order${0..1}");
        // 主键生成配置
        result.setKeyGeneratorConfig(getKeyGeneratorConfigurationForTOrder());
        return result;
    }
    //主键操作的生成策略
    private KeyGeneratorConfiguration getKeyGeneratorConfigurationForTOrder() {
        Properties keyGeneratorProp = getKeyGeneratorProperties();
        return new KeyGeneratorConfiguration("SNOWFLAKE", "order_id", keyGeneratorProp);
    }
    // 获取相关的Order订单规则表配置信息控制配置控制机制
    TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "ds${0..1}.t_order_item${0..1}");
        result.setKeyGeneratorConfig(getKeyGeneratorConfigurationForTOrderItem());
        return result;
    }
    // 创建相关keyOrderItem机制控制操作
  private KeyGeneratorConfiguration getKeyGeneratorConfigurationForTOrderItem() {
        Properties keyGeneratorProp = getKeyGeneratorProperties();
        return new KeyGeneratorConfiguration("SNOWFLAKE", "id", keyGeneratorProp);
    }
  // 生成键值相关的generator的配置信息控制
    private Properties getKeyGeneratorProperties() {
        Properties keyGeneratorProp = new Properties();
        String distributeProcessIdentify = NetUtils.getLocalAddress() + ":" + getProcessId();
        String workId = String.valueOf(convertString2Long(distributeProcessIdentify));
        keyGeneratorProp.setProperty("worker.id", workId);
        LogUtils.info(log, "shardingsphere init", "shardingsphere work id raw string is {}, work id is {}", distributeProcessIdentify, workId);
        return keyGeneratorProp;
    }
    // 数据源相关配置机制
    Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("ds0", DataSourceUtils.createDataSource("ds0"));
        result.put("ds1", DataSourceUtils.createDataSource("ds1"));
        return result;
    }
  // 常见相关的workerid和dataid对应相关的进程id
    private String getProcessId(){
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String pid = name.split("@")[0];
        return pid;
    }
  // 转换字符串成为相关的long类型
    private Long convertString2Long(String str){
        long hashCode = str.hashCode() + System.currentTimeMillis();
        if(hashCode < 0){
            hashCode = -hashCode;
        }
        return hashCode % (1L << 10);
    }
}
复制代码
ShardingsphereMybatisConfig 配置机制
/**
 * @Author zhangboqing
 * @Date 2020/4/23
 */
@Configuration
@MapperScan(basePackages = "com.zbq.springbootshardingjdbcjavaconfigdemo.dao",sqlSessionFactoryRef = "sqlSessionFactoryForShardingjdbc")
public class ShardingsphereMybatisConfig {
    @Autowired
    @Qualifier("shardingDataSource")
    private DataSource dataSource;
    @Bean("sqlSessionFactoryForShardingjdbc")
    public SqlSessionFactory sqlSessionFactoryForShardingjdbc() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
//        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().
//                getResources("classpath*:**/*.xml"));
        sessionFactory.setTypeAliasesPackage("com.zbq.springbootshardingjdbcjavaconfigdemo.domain.entity");
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        sessionFactory.setConfiguration(configuration);
        return sessionFactory.getObject();
    }
}
复制代码



ShardingsphereTransactionConfig 配置机制


主要定制化配置事务操作可以空战未来的,为了未来的查询扩展XA

@Configuration
@EnableTransactionManagement
public class ShardingsphereTransactionConfig {
    @Bean
    @Autowired
    public PlatformTransactionManager shardingsphereTransactionManager(@Qualifier("shardingDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}





相关文章
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2
|
3月前
|
开发框架 负载均衡 Java
当热门技术负载均衡遇上 Spring Boot,开发者的梦想与挑战在此碰撞,你准备好了吗?
【8月更文挑战第29天】在互联网应用开发中,负载均衡至关重要,可避免单服务器过载导致性能下降或崩溃。Spring Boot 作为流行框架,提供了强大的负载均衡支持,通过合理分配请求至多台服务器,提升系统可用性与可靠性,优化资源利用。本文通过示例展示了如何在 Spring Boot 中配置负载均衡,包括添加依赖、创建负载均衡的 `RestTemplate` 实例及服务接口调用等步骤,帮助开发者构建高效、稳定的应用。随着业务扩展,掌握负载均衡技术将愈发关键。
75 6
|
1月前
|
存储 Java API
简单两步,Spring Boot 写死的定时任务也能动态设置:技术干货分享
【10月更文挑战第4天】在Spring Boot开发中,定时任务通常通过@Scheduled注解来实现,这种方式简单直接,但存在一个显著的限制:任务的执行时间或频率在编译时就已经确定,无法在运行时动态调整。然而,在实际工作中,我们往往需要根据业务需求或外部条件的变化来动态调整定时任务的执行计划。本文将分享一个简单两步的解决方案,让你的Spring Boot应用中的定时任务也能动态设置,从而满足更灵活的业务需求。
84 4
|
1月前
|
存储 JSON 算法
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
文章介绍了JWT令牌的基础教程,包括其应用场景、组成部分、生成和校验方法,并在Springboot中使用JWT技术体系完成拦截器的实现。
72 0
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
|
2月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
39 1
|
1月前
|
机器学习/深度学习 移动开发 自然语言处理
基于人工智能技术的智能导诊系统源码,SpringBoot作为后端服务的框架,提供快速开发,自动配置和生产级特性
当身体不适却不知该挂哪个科室时,智能导诊系统应运而生。患者只需选择不适部位和症状,系统即可迅速推荐正确科室,避免排错队浪费时间。该系统基于SpringBoot、Redis、MyBatis Plus等技术架构,支持多渠道接入,具备自然语言理解和多输入方式,确保高效精准的导诊体验。无论是线上医疗平台还是大型医院,智能导诊系统均能有效优化就诊流程。
|
3月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
974 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
3月前
|
NoSQL JavaScript 前端开发
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
文章介绍了如何使用SpringBoot和Vue实现一个校园二手系统,采用前后端分离技术。系统具备完整的功能,包括客户端和管理员端的界面设计、个人信息管理、商品浏览和交易、订单处理、公告发布等。技术栈包括Vue框架、ElementUI、SpringBoot、Mybatis-plus和Redis。文章还提供了部分源代码,展示了前后端的请求接口和Redis验证码功能实现,以及系统重构和模块化设计的一些思考。
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
|
3月前
|
Java 数据库连接 数据库
告别繁琐 SQL!Hibernate 入门指南带你轻松玩转 ORM,解锁高效数据库操作新姿势
【8月更文挑战第31天】Hibernate 是一款流行的 Java 持久层框架,简化了对象关系映射(ORM)过程,使开发者能以面向对象的方式进行数据持久化操作而无需直接编写 SQL 语句。本文提供 Hibernate 入门指南,介绍核心概念及示例代码,涵盖依赖引入、配置文件设置、实体类定义、工具类构建及基本 CRUD 操作。通过学习,你将掌握使用 Hibernate 简化数据持久化的技巧,为实际项目应用打下基础。
196 0
|
3月前
|
Java 前端开发 Spring
技术融合新潮流!Vaadin携手Spring Boot、React、Angular,引领Web开发变革,你准备好了吗?
【8月更文挑战第31天】本文探讨了Vaadin与Spring Boot、React及Angular等主流技术栈的最佳融合实践。Vaadin作为现代Java Web框架,与其他技术栈结合能更好地满足复杂应用需求。文中通过示例代码展示了如何在Spring Boot项目中集成Vaadin,以及如何在Vaadin项目中使用React和Angular组件,充分发挥各技术栈的优势,提升开发效率和用户体验。开发者可根据具体需求选择合适的技术组合。
75 0