首页 > 其他分享 >spring多数据源动态切换的实现原理及读写分离的应用

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

时间:2023-08-10 14:03:18浏览次数:43  
标签:DynamicRoutingDataSource 数据源 数据库 dbName public spring 读写 businessDataSource

简介

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
  1. 读取yml中的主从数据源配置
@Data
@ConfigurationProperties(prefix = "spring.datasource")
@Component
public class DataSourcePropertiesConfig {
    /**
     * 主库配置
     */
    DruidDataSource businessMaster;
    /**
     * 从库配置
     */
    DruidDataSource businessSlaver;
}
  1. 自定义动态数据源类 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();
    }
}
  1. 创建动态数据源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;
    }
}
  1. 自定义一个注解,指定数据库。
    可以将一些常用的查询接口自动路由到读库,以减轻主库压力。
@Documented
@Inherited
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSwitch {
    /**
     * 数据源名称,默认主库
     */
    String dbName() default "master";
}
  1. 定义一个切面,拦截所有 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);
        }
    }
}



标签:DynamicRoutingDataSource,数据源,数据库,dbName,public,spring,读写,businessDataSource
From: https://blog.51cto.com/u_16205813/7034619

相关文章

  • Linux线程同步(try锁和读写锁)
    (文章目录)前言本篇文章继续讲解Linux线程同步,上篇文章讲解了互斥锁,本篇文章为大家讲解try锁和读写锁。一、try锁在Linux的多线程编程中,try锁是一种非阻塞的锁机制,也称为尝试锁。它允许线程尝试获取锁,如果锁当前是可用的,线程将获取到锁并继续执行,如果锁当前被其他线程持有,则线......
  • Spring Boot常见企业开发场景应用、自动配置原理结构分析
     读者应具备:SpringSpringMVC服务器端开发基础Maven基础本篇主要介绍SpringBoot在企业开发中常见场景的使用、以及SpringBoot的基本原理结构。以下为本篇设计的技术应用场景:构建SpringJava程序构建JUnit测试用例构建SpringJDBCTemplate应用程序操作数据库构建Servlet、JSP程......
  • Spring cloud智慧工地源码(项目端+监管端+数据大屏+APP)
    【智慧工地PC项目端功能总览】一.项目人员管理包括:信息管理、信息采集、证件管理、考勤管理、考勤明细、工资管理、现场统计、WIFI教育、工种管理、分包商管理、班组管理、项目管理。1.信息管理:头像、姓名、性别、身份证、进场时间、分包单位、劳务工种、项目履历、是否零工、计......
  • SpringBoot3文件管理
    目录一、简介二、工程搭建1、工程结构2、依赖管理三、上传下载1、配置管理2、上传下载四、Excel文件1、Excel创建2、Excel读取3、解析监听4、导入导出五、参考源码标签:上传.下载.Excel.导入.导出;一、简介在项目中,文件管理是常见的复杂功能;首先文件的类型比较多样,处理起来比......
  • 10、Spring之AOP概述
    10.1、概念AOP(AspectOrientedProgramming)是一种设计思想,是软件设计领域中的面向切面编程AOP是面向对象编程(OOP)的一种补充和完善,OOP是纵向继承机制,AOP是横向抽取机制AOP能通过预编译方式和运行期动态代理方式,实现在不修改源代码的情况下动态地为程序添加统一的附加功能......
  • spring-mvc 系列:拦截器和异常处理器
    目录一、拦截器的配置二、拦截器的三个抽象方法三、多个拦截器的执行顺序四、基于配置的异常处理器五、基于注解的异常处理器一、拦截器的配置SpringMVC中的拦截器用于拦截控制器方法的执行SpringMVC中的拦截器需要实现HandlerInterceptorSpringMVC的拦截器必须在SpringMVC的......
  • 【Spring】Bean Validation
     参考:https://www.baeldung.com/java-validationhttps://www.baeldung.com/java-bean-validation-not-null-empty-blankhttps://www.baeldung.com/spring-mvc-custom-validator 定制自己的校验器 maven依赖Inthelatestversionof spring-boot-starter-validation,......
  • 解密SpringBoot3.0:构建易维护的JavaWeb应用
    SpringBoot3.0最新深入浅出从入门到项目实战,突出Web应用痛点解决方案SpringBoot已经成为Java开发中最流行的框架之一,它提供了一种快速构建、易于扩展的方式,使开发人员能够更加专注于业务逻辑而不是繁琐的配置。而最新的SpringBoot3.0版本将进一步改善开发体验,并提供更多的解决方......
  • Spring 简介
    Spring是用于企业Java应用程序开发的最流行的应用程序开发框架。全球数百万开发人员使用SpringFramework创建高性能、易于测试和可重用的代码。SpringFramework是一个开源的Java平台。它最初由RodJohnson编写,并于2003年6月在Apache2.0许可下首次发布。Spring在大小和透明度方......
  • Spring Boot 启动流程追踪(第一篇)
    1、初始化SpringApplication publicSpringApplication(ResourceLoaderresourceLoader,Class<?>...primarySources){ this.resourceLoader=resourceLoader; Assert.notNull(primarySources,"PrimarySourcesmustnotbenull"); this.primarySourc......