spring boot+mybatis+druid 的项目接入shardingsphere jdbc
背景:
因为现有项目功能已经比较庞大,前期的架构搭建并不适应当前的需求,但是也没有资源投入进行彻底的改造,针对部分大表面临分片的需求。经过调研对比,轻量、免费、改造容易、运维工作量比较小的选项中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.各种版本不对应引起的方法缺失或者是功能对不上的报错等,这种就比较坑,一个是溯源看源码,一个事翻帖子看大家有没有踩过类似的。
最后大家一起踩坑愉快~
更多推荐
所有评论(0)