相关文章:

  • 自己动手写一个分库分表中间件(一)思考
  • 自己动手写一个分库分表中间件(二)数据源定义和分片代理层设计
  • 排查项目中读写分离失效原因
  • 小议 Java 内省机制

注:本文内容暂不涉及事务相关的问题及配置,事务问题会在后面的文章中重点介绍。

本文主要介绍分库分表最重要的功能,数据路由的实现。

1. 配置解析

数据路由第一步是要解析配置,根据两篇文章的介绍,从物理上,或者说 DataSource 的级别,我们是“business+model”构成一个数据源,但是还有分表的配置,所以逻辑上的数据源是由“business+model+表配置”构成。

配置的解析实现很简单,无非就是解析一下 properties 或者 yml 文件。

这里有个小细节就是如何优雅的把配置文件装配成一个 Java 的对象或者说 Spring 的 Bean。这里可以使用 @ConfigurationProperties 或者基于 Java 内省机制去做。

2. 装配数据源

与 Sharding-JDBC 扩展 JDBC 功能不同,我们还是要强依赖 Spring 的,所以这里动态数据源需要扩展 Spring 的 AbstractRoutingDataSource。Spring 动态数据源的设计是一个标准的模版设计模式。里面有一个 Map<Object, Object> 类型的 targetDataSources 变量,这里的 value 就是 DataSource,key 就是我们定义的 DataSource 级别的数据源标识,我这里就是 business+model。

我们还需要实现AbstractRoutingDataSource 的抽象方法 determineCurrentLookupKey(),从方法名不难看出,这就是告诉 Spring 现在的路由标识 key 是哪个 ,然后 Spring 就会从 targetDataSources 这个 Map 里面取出 DataSource 进而获取 Connection

大致代码如下:

/*** @author Dongguabai* @description* @date 2022-04-10 21:13*/
public class DynamicDataSource extends AbstractRoutingDataSource {//通过 ThreadLocal 传递数据源路由 keypublic static final ThreadLocal<String> KEY_HOLDER = new ThreadLocal<>();@Overrideprotected Object determineCurrentLookupKey() {return KEY_HOLDER.get();}
}
    @Bean@Primarypublic DynamicDataSource dataSource(DataSource first, DataSource second) {Map<Object, Object> targetDataSources = new HashMap<>(5);targetDataSources.put("FIRST", first);targetDataSources.put("SECOND", second);DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);return dynamicDataSource;}

还有一个细节是这里基于 ThreadLocal 进行数据源 key 的传递,相对来说更方便和隐式。

因为我们系统使用的是 MyBatis,所以这里也需要跟 SqlSessionFactor 进行绑定:

    @Beanpublic SqlSessionFactory shardingSqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);return sessionFactory.getObject();}

到这里,动态数据源就已经配置好了。

还有一个问题是我们需要去指定 key,也就是说需要在适当的时机将 key 设置到 DynamicDataSourceKEY_HOLDER 中去。

3. 动态数据源切面

很明显需要使用到 Spring AOP。其实上面第 2 步数据源 Map 我们都有了,第 3 步如何去指定数据源路由 key 完全就可以自由发挥了。

这里说一下我个人的设计思路,动态数据源切面里要解决三个问题:

  1. 开发同学如何设置 key(开发同学如何使用)
  2. 切哪里
  3. KEY_HOLDER 里面到值什么时候 remove

3.1 设置数据源标识

这里提供了两种编程模型:

  1. 注解编程
  2. API 编程

可以提供一个注解:

/*** @author Dongguabai* @description* @date 2022-04-10 21:36*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DynamicRoute {String business() default "";String model() default "slave";
}

DynamicRoute 是标注在方法上,表示这个方法内部的数据库操作路由。那么上面说的 3 个问题都好解决了,直接切被 DynamicRoute 标注的方法,方法执行前解析注解里面的属性,设置到 KEY_HOLDER 中去,然后 DynamicDataSource 就会从 KEY_HOLDER 中拿到数据源 key,获取对应的 Connection

目标方法执行完成后 finally 执行 KEY_HOLDERremove() 即可。

但是这个注解有个问题,太“静态”了,可能更适合读写分离的场景,比如我就希望这个方法查询主库或者报表库,加注解指定一下 model 就行了。但是更多的时候我们会对 SQL 参数去动态的解析,进而确定这个 SQL 要走哪个库。

这种场景就可以再提供一个注解:

/*** @author Dongguabai* @description* @date 2022-03-23 02:14*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface DynamicRoutingKey {RoutingKeyType type() default RoutingKeyType.ORDER_ID;String model() default "slave";
}

这个注解是标注在方法参数上的,比如标注的是订单 Id。解析出标注的订单 Id 后根据我们自己的规则找出 business 和 model,进而确定数据源 key。

再回看上面的三个问题,可以切自己动手写一个分库分表中间件(二)数据源定义和分片代理层设计中提到的代理层中的方法,如果有参数被 DynamicRoutingKey 标注那么就解析设置数据源路由 key 到 KEY_HOLDER 中去。

但有时候使用注解不太方便,所以我们还需要提供一种 API 的方式,可以让使用者更灵活的去设置数据源路由:

/*** @author Dongguabai* @description* @date 2022-04-10 21:37*/
public class ShardingUtils {public static void setIfAbsent(String business, String model) {KEY_HOLDER.set(business+"-"+model);}
}

以上就是三种给开发同学使用的标识数据源路由的方式。

其实实现方式和原理都不难,但是里面还是有一些值得注意的点,这里简单介绍一下。

3.2 实现过程中的注意事项

  1. 优先级问题

    上面提供了三种方式都可以设置路由,由于无法完全限制开发同学的使用,所以万一有个代理层方法标注了DynamicRoute,方法参数也标注了 DynamicRoutingKey,同时方法内部也是用了 API 的方式去设置路由,那么到底以哪个为准呢,其实这是一个设计问题,主要符合我们的目的就行。

    这块我在设计上主要基于两个原则:

    • 实现更方便

    • 更容易理解

    依据这两个原则我的设计是“外层优先”,只要外层指定了路由,那么内层的路由策略均失效,方法上>方法参数上>方法内部。

  2. API 方式的作用范围

    注解我们可以拿到切点,也就是 Method,但是 API 的方式我们是拿不到的,所以 API 设置的路由 key 在哪里被 remove 掉呢。我这里的设计思路和 PageHelper 是一样的,即只有下一个 SQL 才会生效。

  3. 重入问题

    第 1 点解决优先级的问题,但是还有一个问题,三种方式最终都是设置数据到 DynamicDataSourceKEY_HOLDER 中去,但是切面是不一样的,那么就可能出现内部的切面把 KEY_HOLDER 中的数据删除了,导致外层(外层优先)路由设置失效。这里可以在设置 KEY_HOLDER 的同时设置一个标识(或者计数),然后在 finally 执行 remove 的时候根据标识判断是否要删除。

总结

本文主要介绍了基于 Spring 的 AbstractRoutingDataSource 实现动态数据源,即分库分表数据源来实现数据源的动态路由 。正如在第一篇文章自己动手写一个分库分表中间件(一)思考中说的,基于现有的框架的能力,其实分库分表写起来真的不难(当然也可以去扩展 Java JDBC,那样实现的话,整个框架的适用性就更强了,只要是符合 JDBC 规范的 ORM 框架都能去整合,但同样的,实现也会更复杂),更多地是考虑一些细节和使用的便捷性。

欢迎关注公众号:

自己动手写一个分库分表中间件(三)数据源路由实现相关推荐

  1. 分享下去年底写的分库分表中间件heisenberg

    好久没有写博了,去年年底的时候写了一个分库分表中间件服务器,当时正在看绝命毒师,觉得heisenberg这个名字很叼,然后就以这个命名了,炼毒也要精益求精啊... 公司在java分布式这块的基础设施很 ...

  2. 去年底写的mysql分库分表中间件heisenberg

    好久没有写博了,去年年底的时候写了一个分库分表中间件服务器,当时正在看绝命毒师,觉得heisenberg这个名字很叼,然后就以这个命名了,炼毒也要精益求精啊... 公司在java分布式这块的基础设施很 ...

  3. 【数据库与事务系列】分库分表中间件

    前面讲了利用mybatis插件进行多数据源切换和分表的方案,但是对业务侵入性较强,当然给予mybatis-plus的对业务侵入性还好,但是支持的策略有限.场景有限. 所以业界诞生了很多分库分表中间件来 ...

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

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

  5. 一文快速入门分库分表中间件 Sharding-JDBC

    一.Sharding-JDBC 简介 Sharding-JDBC 最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名 ...

  6. 【Sharding-JDBC系列二】一文快速入门分库分表中间件 Sharding-JDBC (必修课)

    作为Sharding-JDBC 分库分表实战系列的开篇文章,我们在前文中回顾了一下分库分表的基础知识,对分库分表的拆分方式有了一定的了解,下边我们介绍一下 Sharding-JDBC框架和快速的搭建一 ...

  7. 关系型数据库分库分表中间件之选型

    写在前面 本文主要介绍关系型数据库分库分表的中间件,主要包含中间件介绍.选项及其对比.虽然市面上很多分库分表中间件,但是大多数都是不友好或者社区活跃度不高的项目,当然还是有很多淘汰的中间件.目前,在实 ...

  8. 数据库分库分表中间件对比(很全)

    数据库(分库分表)中间件对比 分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm. 根据一定的规则把数据文件(MYD)和索引文件( ...

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

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

最新文章

  1. 2019年不可错过的45个AI开源工具,你想要的都在这里
  2. Eclipse 创建 Java 包
  3. 使用JWT实现单点登录(完全跨域方案)
  4. php软件开发--公众平台
  5. 本地虚拟机中匿名ftp上传文件失败的问题
  6. 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1](简单易懂)
  7. php查询mysql增加模板消息_php 实现发送微信模板消息
  8. linux命令行显卡驱动,Linux下NVIDIA显卡驱动安装方法
  9. Chapter 1 贝叶斯推断的思想
  10. [原创]网站文章页面添加分享按钮,百度按钮代码复制使用时不显示问题解决!
  11. 定级阿里P7,300道Java面试题帮你全副武装
  12. python爬虫_网易音乐歌单
  13. 解读大学里的软件工程专业以及其他专业,给高考完的学弟妹一点参考
  14. 小米电视安装 Plex 打造家庭影院
  15. Nginx反向代理到另外一台服务器,域名解析IP变更后连接超时
  16. windows 程序设计 第一章
  17. 【初级C语言】表达式和基本语句(布尔型与0比较,浮点型与0比较,switch语句,提高循环语句的效率)
  18. WGCNA构建基因共表达网络详细教程
  19. 优酷路由宝增加php,优酷路由宝旗舰版YK-L2刷改华硕[N14U N54U]5G 2G的7620老毛子Padavan固件方法...
  20. 6大核心议题首度揭晓,2021下半年最强热点来了! | 2021世界区块链大会·杭州...

热门文章

  1. IE兼容性问题汇总【持续更新中】
  2. c语言和c 自学,自学C语言最常遇到的七个问题
  3. 在JAVA类中解析GOOGLE MAP地址和反向解析纬经度
  4. 利用Idea中Gson插件快速编辑POJO类
  5. EZView如何与网络视频录像机语音对讲
  6. Java实现扫雷(简化版)
  7. 统计学系列:统计、数据与思想
  8. Facebook广告投放经常被问的几个问题
  9. 苏州大学计算机系收分,2016年苏州大学艺术类专业录取分数线
  10. comsol光学仿真01