简介

AbstractRoutingDataSource是Spring框架中的一个抽象类,可以实现多数据源的动态切换和路由,以满足复杂的业务需求和提高系统的性能、可扩展性、灵活性。

应用场景

  1. 多租户支持:对于多租户的应用,根据当前租户来选择其对应的数据源,实现租户级别的隔离和数据存储。
  2. 分库分表:为了提高性能和扩展性,将数据分散到多个数据库或表中,根据分片规则来选择正确的数据源,实现分库分表。
  3. 读写分离:为了提高数据库的读写性能,可能会采用读写分离的方式,根据读写操作的类型来选择合适的数据源,实现读写分离。
  4. 数据源负载均衡:根据负载均衡策略来选择合适的数据源,将请求均匀地分配到不同的数据源上,提高系统的整体性能和可伸缩性。
  5. 多数据库支持:在一些场景下,可能需要同时连接多个不同类型的数据库,如关系型数据库、NoSQL数据库等。根据业务需求选择不同类型的数据源,实现对多数据库的支持。

实现原理

1.AbstractRoutingDataSource实现了DataSource接口,作为一个数据源的封装类,负责路由数据库请求到不同的目标数据源

2.该类中定义了一个determineTargetDataSource方法,会获取当前的目标数据源标识符,进而返回真正的数据源;

值得注意的是:其中determineCurrentLookupKey为抽象方法,明显是要让用户自定义实现获取数据源标识的业务逻辑。

3.当系统执行数据库操作之前,会先获取数据源链接,即调用getConnection方法,该类重写的getConnection方法,会获取到真正的目标数据源,进而将数据库操作委托给目标数据源进行处理。

读写分离实现V1版

  1. yml中配置主从数据库连接信息
spring:
datasource:
business-master:
url: jdbc:mysql://ip1:3306/xxx
username: c_username
password: p1
business-slaver:
url: jdbc:mysql://ip2:3306/xxx
username: c_username
password: p2

2.读取yml中的主从数据源配置

@Data
@ConfigurationProperties(prefix = "spring.datasource")
@Component
public class DataSourcePropertiesConfig {
/**
* 主库配置
*/
DruidDataSource businessMaster;
/**
* 从库配置
*/
DruidDataSource businessSlaver;
}

3.自定义动态数据源类DynamicRoutingDataSource,继承AbstractRoutingDataSource类,并重写determineCurrentLookupKey方法,定义获取目标数据源标识的逻辑。

此处的逻辑为:定义一个DataSourceHolder类,将数据源标识放到ThreadLocal中,当需要时从ThreadLocal中获取。

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
/**
* 获取目标数据源标识
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDbName();
}
}
public class DataSourceHolder {
/**
* 当前线程使用的 数据源名称
*/
private static final ThreadLocal<String> THREAD_LOCAL_DB_NAME = new ThreadLocal<>();
/**
* 设置数据源名称
*/
public static void setDbName(String dbName) {
THREAD_LOCAL_DB_NAME.set(dbName);
}
/**
* 获取数据源名称,为空的话默认切主库
*/
public static String getDbName() {
String dbName = THREAD_LOCAL_DB_NAME.get();
if (StringUtils.isBlank(dbName)) {
dbName = DbNameConstant.MASTER;
}
return dbName;
}
/**
* 清除当前数据源名称
*/
public static void clearDb() {
THREAD_LOCAL_DB_NAME.remove();
}
}

4.创建动态数据源DynamicRoutingDataSource对象,并注入到容器中。这里创建了主从两个数据源,并进行了初始化,分别为其设置了数据源标识并放到了DynamicRoutingDataSource对象中,以便后面使用。

若为多个数据源,可参考此处进行批量定义。

@Configuration
public class DataSourceConfig {
@Autowired
private DataSourcePropertiesConfig dataSourcePropertiesConfig;
/**
* 主库数据源
*/
public DataSource masterDataSource() throws SQLException {
DruidDataSource businessDataSource = dataSourcePropertiesConfig.getBusinessMaster();
businessDataSource.init();
return businessDataSource;
}
/**
* 从库数据源
*/
public DataSource slaverDataSource() throws SQLException {
DruidDataSource businessDataSource = dataSourcePropertiesConfig.getBusinessSlaver();
businessDataSource.init();
return businessDataSource;
}
/**
* 动态数据源
*/
@Bean
public DynamicRoutingDataSource dynamicRoutingDataSource() throws SQLException {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slaver", slaverDataSource());
dynamicRoutingDataSource.setDefaultTargetDataSource(masterDS);
dynamicRoutingDataSource.setTargetDataSources(targetDataSources);
dynamicRoutingDataSource.afterPropertiesSet();
return dynamicRoutingDataSource;
}
}

5.自定义一个注解,指定数据库。

可以将一些常用的查询接口自动路由到读库,以减轻主库压力。

@Documented
@Inherited
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSwitch {
/**
* 数据源名称,默认主库
*/
String dbName() default "master";
}

6.定义一个切面,拦截所有Controller接口,使用DataSourceSwitchRead注解的方法,将统一路由到读库查询

@Aspect
@Component
@Slf4j
public class DataSourceAspect {
/**
* 切库,若为多个从库,可在这里添加负载均衡策略
*/
@Before(value = "execution ( * com.jd.gyh.controller.*.*(..))")
public void changeDb(JoinPoint joinPoint) {
Method m = ((MethodSignature) joinPoint.getSignature()).getMethod();
DataSourceSwitch dataSourceSwitch = m.getAnnotation(DataSourceSwitch.class);
if (dataSourceSwitch == null) {
DataSourceHolder.setDbName(DbNameConstant.MASTER);
log.info("switch db dbName = master");
} else {
String dbName = dataSourceSwitch.dbName();
log.info("switch db dbName = {}", dbName);
DataSourceHolder.setDbName(dbName);
}
}
}

作者:京东科技 郭艳红

来源:京东云开发者社区

spring多数据源动态切换的实现原理及读写分离的应用的更多相关文章

  1. Spring多数据源动态切换

    title: Spring多数据源动态切换 date: 2019-11-27 categories: Java Spring tags: 数据源 typora-root-url: ...... --- ...

  2. spring 多数据源动态切换

    理解spring动态切换数据源,需要对spring具有一定的了解 工作中经常遇到读写分离,数据源切换的问题,那么以下是本作者实际工作中编写的代码  与大家分享一下! 1.定义注解 DataSource ...

  3. Spring主从数据源动态切换

    参考文档: http://uule.iteye.com/blog/2126533 http://lanjingling.github.io/2016/02/15/spring-aop-dynamicd ...

  4. 实战:Spring AOP实现多数据源动态切换

    需求背景 去年底,公司项目有一个需求中有个接口需要用到平台.算法.大数据等三个不同数据库的数据进行计算.组装以及最后的展示,当时这个需求是另一个老同事在做,我只是负责自己的部分. 直到今年回来了,这个 ...

  5. Spring Boot 如何动态切换数据源

    本章是一个完整的 Spring Boot 动态数据源切换示例,例如主数据库使用 lionsea 从数据库 lionsea_slave1.lionsea_slave2.只需要在对应的代码上使用 Data ...

  6. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  7. Springboot多数据源配置--数据源动态切换

    在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...

  8. springboot多数据源动态切换和自定义mybatis分页插件

    1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.d ...

  9. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  10. mybatis 多数据源动态切换

    笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心. gateway网关过滤.admin服务监控.auth授权体系验证,集成了redi ...

随机推荐

  1. Go windows 环境搭建

    下载地址 官网下载地址:https://golang.google.cn/dl/ 1.下载完之后 双击msi进行安装 路径可以不用改, 继续next 安装完之后就需要配置环境变量, 找到环境变量 GO ...

  2. 2022-10-25:在一个 2 * 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字(上下左右)进行交换.

    2022-10-25:在一个 2 * 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.一次 移动 定义为选择 0 与一个相邻的数字(上下左右)进行交换. ...

  3. 2022-07-16:以下go语言代码输出什么?A:[];B:[5];C:[5 0 0 0 0];D:[0 0 0 0 0]。 package main import ( “fmt“ )

    2022-07-16:以下go语言代码输出什么?A:[]:B:[5]:C:[5 0 0 0 0]:D:[0 0 0 0 0]. package main import ( "fmt" ...

  4. 2022-03-07:K 个关闭的灯泡。 N 个灯泡排成一行,编号从 1 到 N 。最初,所有灯泡都关闭。每天只打开一个灯泡,直到 N 天后所有灯泡都打开。 给你一个长度为 N 的灯泡数组 blubs

    2022-03-07:K 个关闭的灯泡. N 个灯泡排成一行,编号从 1 到 N .最初,所有灯泡都关闭.每天只打开一个灯泡,直到 N 天后所有灯泡都打开. 给你一个长度为 N 的灯泡数组 blubs ...

  5. Netty(1)——NIO基础

    本篇主要介绍Java NIO的基本原理和主要组件 Netty是由JBOSS提供的Java开源网络应用程序框架,其底层是基于Java提供的NIO能力实现的.因此为了掌握Netty的底层原理,需要首先了解 ...

  6. 认识 CPU 底层原理(2)——逻辑门

    本文为B站UP主硬件茶谈制作的系列科普<[硬件科普]带你认识CPU>系列的学习笔记,仅作个人学习记录使用,如有侵权,请联系博主删除 上一篇文章我们从最基本的粒子的角度认识了组成CPU的最基 ...

  7. Python从0到1丨带你认识图像平滑的三种线性滤波

    摘要:常用于消除噪声的图像平滑方法包括三种线性滤波(均值滤波.方框滤波.高斯滤波)和两种非线性滤波(中值滤波.双边滤波),本文将详细讲解三种线性滤波方法. 本文分享自华为云社区<[Python从 ...

  8. SignalR WebSocket通讯机制

    1.什么是SignalR ASP.NET SignalR 是一个面向 ASP.NET 开发人员的库,可简化向应用程序添加实时 Web 功能的过程. 实时 Web 功能是让服务器代码在可用时立即将内容推 ...

  9. MAUI Android 关联文件类型

    实现效果 打开某个文件,后缀是自己想要的类型,在弹出的窗口(用其它应用打开)的列表中显示自己的应用图标 点击后可以获得文件信息以便于后续的操作 实现步骤 以注册.bin后缀为例,新建一个MAUI项目 ...

  10. Galaxy Project | 一些尝试与思考

    很久都没有更新推文了,脑壳羞涩,快码不出字的节奏! 最近在尝试内部 Galaxy 一些新工具的开发和 Galaxy 核心版本的升级测试,发现一些问题,简单记录和聊一下吧. 一些尝试 对于在线的 web ...