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

spring,数据源,动态,切换,实现,原理,读写,分离,应用 · 浏览次数 : 362

小编点评

**AbstractRoutingDataSource** 是一个抽象类,用于实现多数据源的动态切换和路由。它提供了以下主要方法: * **determineCurrentLookupKey()**:获取当前数据源标识符,用于路由请求到正确的目标数据源。 * **afterPropertiesSet()**:在数据源配置完成后执行的回调方法。 **DynamicRoutingDataSource** 是一个实现 **AbstractRoutingDataSource** 的具体类,它使用 **ThreadLocal** 来存储当前数据源标识符,并在请求处理之前从 **ThreadLocal** 中获取。 **主要成员:** * **masterDataSource()**:返回主库的数据源。 * **slaverDataSource()**:返回从库的数据源。 * **dynamicRoutingDataSource()**:返回动态数据源,实现多数据源支持。 **配置** 可以通过 **DataSourcePropertiesConfig** 类配置数据源,例如主库和从库的连接信息。 **使用** 通过在方法上使用 **@DataSourceSwitch** 注解指定数据源名称,可以动态路由请求。例如: ```java @Autowired private DynamicRoutingDataSource dynamicRoutingDataSource; @GetMapping("/findAll") @DataSourceSwitch(value = "master") public List findAll() { // ... } ``` **其他** * **DataSourceHolder** 类用于存储当前数据源标识符。 * **DataSourceSwitch** 注解用于指定数据源名称。 * **@Aspect** 注解用于切面,拦截所有控制器接口,使用 **DataSourceSwitchRead** 方法进行数据源切换。

正文

简介

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

应用场景

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

实现原理

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

AbstractRoutingDataSource hierarchy.png

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

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

determineTargetDataSource.png

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

getConnection.png

读写分离实现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多数据源动态切换的实现原理及读写分离的应用相似的内容:

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

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

一种实现Spring动态数据源切换的方法

## 1 目标 不在现有查询代码逻辑上做任何改动,实现dao维度的数据源切换(即表维度) ## 2 使用场景 节约bdp的集群资源。接入新的宽表时,通常uat验证后就会停止集群释放资源,在对应的查询服务器uat环境时需要查询的是生产库的表数据(uat库表因为bdp实时任务停止,没有数据落入),只进行

实例讲解Spring boot动态切换数据源

摘要:本文模拟一下在主库查询订单信息查询不到的时候,切换数据源去历史库里面查询。 本文分享自华为云社区《springboot动态切换数据源》,作者:小陈没烦恼 。 前言 在公司的系统里,由于数据量较大,所以配置了多个数据源,它会根据用户所在的地区去查询那一个数据库,这样就产生了动态切换数据源的场景。

Spring Boot应用中如何动态指定数据库,实现不同用户不同数据库的场景

当在 Spring Boot 应用程序中使用Spring Data JPA 进行数据库操作时,配置Schema名称是一种常见的做法。然而,在某些情况下,模式名称需要是动态的,可能会在应用程序运行时发生变化。比如:需要做数据隔离的SaaS应用。 所以,这篇博文将帮助您解决了在 Spring Boot

跟我一起学习和开发动态表单系统-前端用vue、elementui实现方法(3)

基于 Vue、Element UI 和 Spring Boot + MyBatis 的动态表单系统前端实现解析 在现代企业信息系统中,动态表单是一种非常常见的功能。它可以根据业务需求灵活地调整表单结构,以满足不同的数据收集和展示需求。在本文中,我们将探讨一种基于 Vue、Element UI 和 S

[转帖]spring 多数据源的使用

目录作者:@dwtfukgv本文为作者原创,转载请注明出处:https://www.cnblogs.com/dwtfukgv/p/14848407.htmlspring 多数据源的使用在同一个项目中需要使用多个数据源,这就需要根据不同的场景进行切换数据源,spring给我们提供一种很方便的方式,那就

Spring Boot 配置多数据源

Spring Boot 配置多数据源 作者:Grey 原文地址: 博客园:Spring Boot 配置多数据源 CSDN:Spring Boot 配置多数据源 说明 本文主要介绍了 Spring Boot 下如何配置多数据源。 环境和版本 Java 版本:17 Spring Boot 版本:3.0.

苞米豆的多数据源 → dynamic-datasource-spring-boot-starter,挺香的!

开心一刻 2023年元旦,我妈又开始了对我的念叨 妈:你到底想多少岁结婚 我:60 妈:60,你想找个多大的 我:找个55的啊,她55我60,结婚都有退休金,不用上班不用生孩子,不用买车买房,成天就是玩儿 我:而且一结婚就是白头偕老,多好 我妈直接一大嘴巴子呼我脸上 需求背景 最近接到一个需求,需要

实战指南,SpringBoot + Mybatis 如何对接多数据源

本文分享自华为云社区 《实战指南,SpringBoot + Mybatis 如何对接多数据源》,作者:战斧。 在我们开发一些具有综合功能的项目时,往往会碰到一种情况,需要同时连接多个数据库,这个时候就需要用到多数据源的设计。而Spring与Myabtis其实做了多数据源的适配,只需少许改动即可对接多

基于Spring-AOP的自定义分片工具

作者:陈昌浩 1 背景 随着数据量的增长,发现系统在与其他系统交互时,批量接口会出现超时现象,发现原批量接口在实现时,没有做分片处理,当数据过大时或超过其他系统阈值时,就会出现错误。由于与其他系统交互比较多,一个一个接口做分片优化,改动量较大,所以考虑通过AOP解决此问题。 2 Spring-AOP