背景:

        因为现有项目功能已经比较庞大,前期的架构搭建并不适应当前的需求,但是也没有资源投入进行彻底的改造,针对部分大表面临分片的需求。经过调研对比,轻量、免费、改造容易、运维工作量比较小的选项中shardingsphere jdbc算是非常友好的选择。

在接入的过程中发现适用的文章\文档不是很多(当然不排除我找的方法不对,或者是因为工期非常紧张所以没有全面的去看官方的所有文档),而且现有的大都是以新项目单纯引入shardingsphere jdbc为目的进行记录和讲解,兼容旧的项目以及更灵活的配置部分几乎没怎么翻到。官方的文档也是只简单的说明了怎么引入,所以踩了超级多的坑,记录下最终根据我们现有项目的解决方案。

关键说明:

  • shardingsphere jdbc 5.4.1版本,当前时间下的最新正式版本
  • spring-boot-starter-validation 2.4.5版本
  • snakeyaml 1.3.3 版本(这里因为默认的版本接入shardingsphere jdbc yaml配置 会报很多方法没有的错误,所以特意提出来说明下,调整版本到不缺方法的地步)
  • 我现有的项目是使用的druid多数据源,每个数据源的区分通过配置文件根据路由自动扫描到对应的数据源,为了减少变动点我接入也是保持了这样的思路。
  • 调整后,如果sql没有太复杂,配置和结构调整完,代码部分是属于不动就行,具体的要根据自己的业务进行测试。
  • 我这里只有部分数据源需要进行分片,其他不需要,包括activiti的独立数据源和quartz的独立数据源如果都放到shardingsphere中去配置的话就不能自动获取到了(这里也是踩了很久的坑),需要单独配置,所以我还是保持多数据源,只是把shardingsphere当做其中的一个数据源进行了配置。
  • 该文章只列出了关键部分,探索过程和很多详情是省略的,我是试了很多个版本解决了N个问题最终成了后总结的关键点。所以如果是刚开始研究,对spring boot的数据源加载不熟悉,对shardingsphere也还完全没了解的看具体的步骤可能会有很多疑问。

具体步骤:

1.pom引入包

这里只放了关键的部分,spring boot mybatis 和druid的引入巨多参考,且旧项目一般都有,就不放进来了。jaxb部分是我看其他文章有提到新版本的shardingsphere没有集成进来要自己引入以免启动报错,这里我就直接引入了,当前还没尝试不引入会不会有问题。

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core</artifactId>
    <version>5.4.1</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.33</version>
</dependency>

2.yaml配置

这里踩坑比较多,新旧版本的配置差异不小,具体的配置项建议以官方版本的为准,5.4.1版本的YamlShardingSphereDataSourceFactory有提供专门的yaml文件整个去创建数据连接的方法,所以将shardingsphere jdbc的配置单独放一个yaml文件,配置多数据源的时候就不用单独取配置挨个进行创建了。

下面的数据源配置我省略了很多连接的配置项,不是必须,根据自己的的服务和数据库情况调优部分直接省略了,但是是支持的,根据自己的类型保持原来配置放进来就可以。我这里单库分表的简单场景,涉及少部分配置项。具体的配置参考官方文档就比较靠谱了,只是官方的用户手册部分没有给出自定义分表算法类的例子,我这里刚好就是,可以参考。

# 数据源配置
dataSources:
  db1:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123
  
# 规则配置
rules:
  - !SHARDING
    tables:
      # 逻辑表名
      table1:
        actualDataNodes: db1.table1_${0..2}
        # 分表策略
        tableStrategy:
          standard:
            # 分片列名称
            shardingColumn: time_date
            # 分片算法名称
            shardingAlgorithmName: table1
    # 分片算法配置
    shardingAlgorithms:
      # 特殊的自定义算法
      table1:
        # 自定义算法,这里引入了自己写的分表规则算法
        type: CLASS_BASED
        props:
          strategy: STANDARD
          algorithmClassName: com.demo.sharding.config.sharding.table1.Talbe1Algorithm

# 属性配置,调试状态下打开即可,方便看有没有分表成功,生产环境关闭
props:
  sql-show: true

分表算法类:

package com.demo.sharding.config.sharding.table1;

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.util.*;

public class MTimeWorkRecordAlgorithm implements StandardShardingAlgorithm<Integer> {

/*这里是精确查询的分表规则*/

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
        Integer timeInt = preciseShardingValue.getValue();
//这里是理我根据我们自己的报错对象抛出的异常,所以直接屏蔽了
       // if(null == timeInt){
            //throw new RRException("分表关键字段异常,请检查");
       // }
//这里我写了工具类,获取前缀和后缀部分的,拼接返回具体的表明,不具体展示了,逻辑部分大家根据自己的规则写就可以
        Integer suffix = MTimeWorkUtil.getSuffix(timeInt);
        return MTimeWorkUtil.getTablePre()+"_"+suffix;
    }

/*这里是范围查询的分表规则*/
    @Override
    public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<Integer> params) {
        Collection<String> collect = new ArrayList<>();//返回数据库节点名称list
        Range<Integer> valueRange = params.getValueRange();//获取查询条件中范围值
        Integer slowerEndpoint = valueRange.hasLowerBound() ? valueRange.lowerEndpoint() : 0;//查询下限值
        Integer supperEndpoint = valueRange.hasUpperBound() ? valueRange.upperEndpoint() : 0;//查询上限值
       //具体逻辑部分已删除,大家自己写即可,这里取出了关键的分表字段以及返回结果等供大家参考
            return collect;
        }
    }

}

数据源配置类:

package com.demo.sharding.config.datasource;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import tk.mybatis.spring.annotation.MapperScan;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;

@Slf4j
@Configuration
//这里的路由大家根据自己的模块配置就行,我是这个路由下全是走sharding jdbc的,其他的配置了类似的其他数据源文件。
@MapperScan(basePackages = "com.demo.sharding.shardingmodules.*.dao", sqlSessionTemplateRef = "shardingSqlSessionTemplate")
public class ShardingDataSourceConfig {
    @Bean
 
    public DataSource shardingDataSource() throws SQLException, IOException {
        return YamlShardingSphereDataSourceFactory.createDataSource(new File(ClassLoader.getSystemResource("sharding.yaml").getPath()));
//这里我的配置文件就是直接叫sharding.yaml,放resource目录下了,里面内容就是上面yaml配置部分
    }


    @Bean(name="shardingJdbcTemplate")
    public JdbcTemplate secondaryJdbcTemplate(@Qualifier("shardingDataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "shardingSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("shardingDataSource") DataSource ds) throws Exception{
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        sessionFactoryBean.setConfiguration(configuration);
        sessionFactoryBean.setDataSource(ds);
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            //自定义mapper的xml文件地址,当通用mapper提供的默认功能无法满足我们的需求时,可以自己添加实现,与mybatis写mapper一样
            sessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/shardingmapper/**/*.xml"));
//这里要切换自己的xml文件位置
            return sessionFactoryBean.getObject();
        } catch (Exception e) {
            log.error("配置second的SqlSessionFactory失败,error:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }

    }

    @Bean(name = "shardingSqlSessionTemplate")
    public SqlSessionTemplate shardingJdbcTemplate(@Qualifier("shardingSqlSessionFactory")SqlSessionFactory dataSource) {
        return new SqlSessionTemplate(dataSource);
    }

    @Bean(name = "shardingTransactionManager")
    public PlatformTransactionManager masterTransactionManager(@Qualifier("shardingDataSource")DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        // 注入dataSource
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

其他数据源部分yml依旧在spring.datasource下照旧配置,配置类也是跟上面类似,只是换路径和数据源创建部分,因为是比较成熟稳定的不部分了,不单独放出来,大家需要可以直接到多数据源配置的专题查看解决后再接入。

代码部分,我这里的是不需要改动直接运行成功的,具体的复杂业务场景还没测试,后续有坑会继续出帖子。


踩坑点:

1.用4.1.1版本的配置直接引入后没报错以为成功了,但是其实只是读进去了配置部分,初始化了。数据源还是根据外面配置的其他数据源部分走的,多数据源且有独立数据源的模式下不改造根本就走不了sharding的数据源,运行成功是个假象,表还是没有按照配置规则走。

2.引入后项目启动过程是个非常漫长的研究过程,因为会出现各种各样的报错,而且报错内容都翻不到帖子。最好的解决方法就是看具体的报错内容,逐个去解决,因为我着急搞好,所以每个错误解决没有具体的记录错误,但是总结下来大概有以下几类

  • A.配置项无法匹配,配置项的校验比较严格,多、少、对不上都会报错,这种就是看具体的报错缺什么就补什么,匹配不上的就翻文档,什么驼峰、-之类的问题,什么类型不匹配之类的,根据报错找解决就是。
  • B.启动会包括很多其他工具,比如我们项目的activiti数据源,quartz的数据源,因为原来spring boot能自动给过去,如果全部倒shardingsphere的话就不能,需要自己单独配置,当然我最终的解决方案是没有把这些放到shardingsphere中去管理。
  • C.shardingsphere有个很坑的点的就是所有的表都需要配置,这个是我坚持使用独立数据源的原因,因为库、表量非常多每个都配置下那就很坑,就算是同一个库也可以配置成不同的数据源的,将需要分片处理的配置到shardingsphere数据源就行,其他不需要处理也不用担sql支持问题。
  • D.各种版本不对应引起的方法缺失或者是功能对不上的报错等,这种就比较坑,一个是溯源看源码,一个事翻帖子看大家有没有踩过类似的。

最后大家一起踩坑愉快~

GitHub 加速计划 / druid / druid
27.83 K
8.56 K
下载
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
最近提交(Master分支:3 个月前 )
f060c270 - 8 天前
1613a765 * Improve gaussdb ddl parser * fix temp table 10 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐