开发者

SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

开发者 https://www.devze.com 2025-08-15 11:25 出处:网络 作者: 菠萝蚊鸭
目录1、依赖2、application.yml3、多数据源 Duid 自动装配详解3.1、自动装配 spring.datasource.dynamic3.2、自动装配多数据源 datasource3.3、自动装配每个数据源下的 duid 配置3.4、自动装配 StatViewServlet3.5、
目录
  • 1、依赖
  • 2、application.yml
  • 3、多数据源 Duid 自动装配详解
    • 3.1、自动装配 spring.datasource.dynamic
    • 3.2、自动装配多数据源 datasource
    • 3.3、自动装配每个数据源下的 duid 配置
    • 3.4、自动装配 StatViewServlet
    • 3.5、自动装配 WebStatFilter
    • 3.6、自动装配 AopPatterns
    • 3.7、Filter 配置类
  • 4、Druid 监控页面
    • 4.1、首页
    • 4.2、数据源列表
    • 4.2、SQL 监控
    • 4.3、SQL 防火墙
    • 4.4、Web 应用
    • 4.5、URI 监控
    • 4.6、Web Session 监控
    • 4.7、Spring 监控
    • 4.8、jsON API
  • 5、动态切换数据源
    • 5.1、数据源枚举
    • 5.2、Controller 层
    • 5.3、AOP 根据入参自动切换数据源
  • 6、日志输出效果

    1、依赖

    <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
         <version>4.3.1</version>
         <exclusions>
             <exclusion>
                 <groupId>com.alibaba</groupId>
                 <artifactId>druid-spring-boot-starter</artifactId>
             </exclusion>
         </exclusions>
     </dependency>
     <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>1.2.24<</version>
     </dependency>

    2、application.yml

    特别注意:多数据源 druid 要配置到每一个数据源里面

    spring:
      application:
        name: DynamicDatasource
      datasource:
        druid:
          stat-view-servlet:
            enabled: true
            allow:
            deny:
            reset-enable: false
            login-username: admin
            login-password: 123456
            url-pattern: /druid/*
          web-stat-filter:
            enabled: true
            #对这些请求放行
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
            #拦截该项目下的一切请求
            url-pattern: /*
            session-stat-enable: true
            principal-session-name: session_name
            principal-cookie-name: cookie_name
            profile-enable: true
          # 包匹配,表示匹配以com.github.wxhnyfy.dynamicdatasource开头的包名,多个用逗号隔开
          aop-patterns: com.github.wxhnyfy.dynamicdatasource.*
        dynamic:
          primary: master # 设置默认的数据源或者数据源组,默认值即为 master
          datasource:
            master:
              #设置默认的数据源
              driverClassName: com.mysql.cj.jdbc.Driver
              url: jdbc:mysql://localhost:3306/region?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&allowpublicKeyRetrieval=true&serverTimezone=GMT%2B8
              username: root
              password: 123456
              type: com.alibaba.druid.pool.DruidDataSource
              # druid连接池设置
              druid:
                # 配置初始化线程数
                initialSize: 5
                # 最小线程数
                minIdle: 5
                # CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
                maxActive: 11
                # 最大等待时间,内网:800,外网:1200(三次握手1s)
                maxWait: 60000
                # 连接可空闲存活时间(ms)
                timeBetweenEvictionRunsMillis: 60000
                # 连接保持空闲而不被驱逐的最长存活时间(ms)
                minEvictableIdleTimeMillis: 300000
                # 用来检测连接是否有效的sql,如果validationQuery为空,那么testOnBorrow、testOnReturn、testWhileIdle这三个参数都不会起作用
                validationQuery: SELECT 1
                # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效;
                testWhileIdle: true
                # 建议配置为false,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
                testOnBorrow: false
                # 建议配置为false,归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能;
                testOnReturn: false
                # PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
                poolPreparedStatements: false
                # 保持minIdle数量的长连接
                keepAlive: true
                # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
                # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。缺省值为-1
                # 开启poolPreparedStatments后生效
                maxPoolPreparedStatementPerConnectionSize: 20
                # 是否合并多个DruidDataSource的监控数据
                useGlobalDataSourceStat: true
                # 配置监控统计拦截的filters
                filters: stat,wall,slf4j
                stat:
                  logSlowSql: true
                  slowSqlMillis: 2000
                  mergeSql: true
                  dbType: mysql
                wall:
                  # 可配置项参考类:com.alibaba.druid.wall.WallConfig
                  multi-statement-allow: true
                  alter-table-allow: false
                  drop-table-allow: false
                slf4j:
                  # 只有当 isStatementExecutableSqlLogEnable() isStatementLogEnabled() 都为ture的情况才打印 可执行sql
                  # 在{# LogFilter#logExecutableSql }使用
                  statement-executable-sql-log-enable: true
                  # 在{# LogFilter#logExecutableSql }使用
                  statement-log-enabled: true
                  statement-create-after-log-enabled: false
                  statement-log-error-enabled: true
                  result-set-log-enabled: false
                  #statementPrepareAfterLogEnable
                  # 准备好的sql语句打印(此时为执行前)未进行参数拼接
                  statement-prepare-after-log-enabled: false
                  #isStatementParameterSetLogEnabled
                  #打印参数
                  statement-parameter-set-log-enabled: false
                  #statementExecuteAfterLogEnable
                  #sql语句执行完成后打印(执行后)未进行参数拼接
                  statement-execute-after-log-enabled: false
                  #statementCloseAfterLogEnable
                  statement-close-after-log-enabled: false
                  #不打印清除参数日志
                  statement-parameter-clear-log-enable: false
                # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
                connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
            slave:
              url: jdbc:mysql://localhost:3307/region?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
              username: root
              password: 123456
              driver-class-name: com.mysql.cj.jdbc.Driver
              type: com.alibaba.druid.pool.DruidDataSource
              # druid连接池设置
              druid:
                # 配置初始化线程数
                initialSize: 5
                # 最小线程数
                minIdle: 5
                # CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
                maxActive: 11
                # 最大等待时间,内网:800,外网:1200(三次握手1s)
                maxWait: 60000
                # 连接可空闲存活时间(ms)
                timeBetweenEvictionRunsMillis: 60000
                # 连接保持空闲而不被驱逐的最长存活时间(ms)
                minEvictableIdleTimeMillis: 300000
                # 用来检测连接是否有效的sql,如果validationQuery为空,那么testOnBorrow、testOnReturn、testWhileIdle这三个参数都不会起作用
                validationQuery: SELECT 1
                # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效;
                testWhileIdle: true
                # 建议配置为false,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
                testOnBorrow: false
                # 建议配置为false,归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能;
                testOnReturn: false
                # PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
                poolPreparedStatements: false
                # 保持minIdle数量的长连接
        android        keepAlive: true
                # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
                # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。缺省值为-1
                # 开启poolPreparedStatments后生效
                maxPoolPreparedStatementPerConnectionSize: 20
                # 是否合并多个DruidDataSource的监控数据
                useGlobalDataSourceStat: true
                # 配置监控统计拦截的filters
                filters: stat,wall,slf4j
                stat:
                  logSlowSql: true
                  slowSqlMillis: 2000
                  mergeSql: true
                  dbType: mysql
                wall:
                  # 可配置项参考类:com.alibaba.druid.wall.WallConfig
                  multi-statement-allow: true
                  alter-table-allow: false
                  drop-table-allow: false
                slf4j:
                  # 只有当 isStatementExecutableSqlLogEnable() isStatementLogEnabled() 都为ture的情况才打印 可执行sql
                  # 在{# LogFilter#logExecutableSql }使用
                  statement-executable-sql-log-enable: true
                  # 在{# LogFilter#logExecutableSql }使用
                  statement-log-enabled: true
                  statement-create-after-log-enabled: false
                  statement-log-error-enabled: true
                  result-set-log-enabled: false
                  #statementPrepareAfterLogEnablejs
                  # 准备好的sql语句打印(此时为执行前)未进行参数拼接
                  statement-prepare-after-log-enabled: false
                  #isStatementParameterSetLogEnabled
                  #打印参数
                  statement-parameter-set-log-enabled: false
                  #statementExecuteAfterLogEnable
                  #sql语句执行完成后打印(执行后)未进行参数拼接
                  statement-execute-after-log-enabled: false
                  #statementCloseAfterLogEnable
                  statement-close-after-log-enabled: false
                  #不打印清除参数日志
                  statement-parameter-clear-log-enable: false
                # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
                connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000

    3、多数据源 Duid 自动装配详解

    3.1、自动装配 spring.datasource.dynamic

    由于我们引入了 dynamic-datasource-spring-boot-starter,启动时会优先自动装配 com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration,并且在类 com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure 之前装配

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    会自动装配以下属性,参考类 com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties,datasource 就是我们配置的多数据源

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.2、自动装配多数据源 datasource

    参考类 com.baomidou.dynamic.datasource.creator.DataSourceProperty

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.3、自动装配每个数据源下的 duid 配置

    这个配置和 Druid 的层级稍有一些不同

    参考类 com.baomidou.dynamic.datasource.creator.druid.DruidConfig

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    数据源的 Filter 配置

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.4、自动装配 StatViewServlet

    参考类:com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.5、自动装配 WebStatFilter

    参考类:com.alibaba.druid.spring.boot.autoconfigure.stat.DruidwebStatFilterConfiguration

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.6、自动装配 AopPatterns

    参考类:com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    3.7、Filter 配置类

    • wall: com.alibaba.druid.wall.WallConfig
    • slf4j、log4j、log4j2、commonsLog: 四个日志 filter 只有实现不同,配置类都是同一个,com.alibaba.druid.filter.logging.LogFilter
    • stat:com.alibaba.druid.filter.stat.StatFilter

    日志 Filter 要引入对应的依赖,Druid 的 LoggerName 在类 com.alibaba.druid.filter.logging LogFilter,配置日志打印级别时需要使用下面的 LoggerName

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4、Druid 监控页面

    4.1、首页

    访问地址:http://IP:端口/上下文/druid/index.html

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.2、数据源列表

    要看每一个数据源的 filter 类名,看一下是否和启用的 Filter 一致,再看一起其他的配置是否能正常读取

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.2、SQL 监控

    执行过 SQL 后,Druid 能对 SQL 进行监控,但是不能区分是哪个数据源执行的。由于我们开启了SQL合并,所以看到的SQL都会带有占位符

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    点击去能看到每条SQL的执行详情。

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.3、SQL 防火墙

    能对黑白名单进行统计,但是不能区分是哪个数据源执行的

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    示例,我们在 spring.datasource.dynamic.datasource.master.wall 配置了不能执行 select 语句

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    编程客栈

    SQL 会执行报错

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    能正常监控到黑名单 SQL

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.4、Web 应用

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.5、URI 监控

    能监控到执行SQL的请求

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.6、Web Session 监控

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.7、Spring 监控

    根据 aop-pattern 配置包名进行监控

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    4.8、JSON API

    把各个页面的信息转为 JSON 数据

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    5、动态切换数据源

    这里给出一个参考解决方案

    5.1、数据源枚举

    public enum DataSouandroidrceEnum {
        MASTER("master"),
        SLAVE("slave");
        private String value;
        DataSourceEnum(String value) {
            this.value = value;
        }
        public String getValue() {
            return value;
        }
    }

    5.2、Controller 层

    在 Controller 的每个方法入参都加上数据源枚举

    @ApiOperation(value = "执行自定义SQL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "envCode", value = "数据源标识", required = true, paramType = "query", example = "MASTER", dataTypeClass = DataSourceEnum.class),
            @ApiImplicitParam(name = "query", value = "查询字符串", required = true, paramType = "query", example = "select * from chinapostoffice编程客栈 limit 100", dataTypeClass = String.class),
    })
    @GetMapping("/getChinaPostOffice ")
    public Response<JSONArray> getChinaPostOffice(@RequestParam DataSourceEnum envCode,
                                                  @RequestParam String query
    ) {
        logger.info(">>> query: {}", query);
        List<Map<String, Object>> list = customMapper.selectList(query);
        logger.info(">>> 总共 {} 条数据", list.size());
        logger.info(">>> 数据: {}", JSONArray.from(list, JSONWriter.Feature.WriteMapNullValue).toJSONString(JSONWriter.Feature.WriteMapNullValue));
        return new Response<JSONArray>().success(JSONArray.from(list, JSONWriter.Feature.WriteMapNullValue));
    }

    5.3、AOP 根据入参自动切换数据源

    DynamicDataSourceContextHolder 实现原理,可参考源码,解析得很详细了

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    @ASPect
    @Component
    // 确保在事务切面之前执行
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class AutoSwitchDataSourceAop {
        private static final Logger logger = LoggerFactory.getLogger(AutoSwitchDataSourceAop.class);
        /**
         * 当前指定的默认数据源
         */
        @Value("${spring.datasource.dynamic.primary}")
        private String dynamicPrimaryDataSource;
        /**
         * 定义匹配需要切换数据源的方法的切点
         * 匹配com.github.wxhnyfy.dynamicdatasource.controller包、com.github.wxhnyfy.dynamicdatasource.impl.service包下所有包含DataSourceEnum参数的方法
         * 支持DataSourceEnum参数在任意参数位置(不限于第一个参数)
         *
         * @param joinPoint 切点
         * @return 目标执行方法
         * @throws Throwable 异常
         */
        @Around("execution(* com.github.wxhnyfy.dynamicdatasource.controller.*.*(com.github.wxhnyfy.dynamicdatasource.enums.DataSourceEnum, ..)) " +
             "|| execution(* com.github.wxhnyfy.dynamicdatasource.impl.service.*.*(com.github.wxhnyfy.dynamicdatasource.enums.DataSourceEnum, ..))")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String methodName = signature.getMethod().getName();
            logger.info("进入 SwitchDataSourceAspect 的方法名: {}", methodName);
            logger.debug("当前指定的默认数据源:{}", dynamicPrimaryDataSource);
            DataSourceEnum envCode = null;
            try {
                // 获取方法参数中的EnvCode值
                envCode = extractEnvCode(joinPoint);
                // 切换数据源
                if (envCode != null) {
                    logger.info("数据库环境标识:{}", envCode);
                    String previousDs = DynamicDataSourceContextHolder.peek();
                    logger.info("方法 [{}] 切换数据源: {} -> {}", methodName, previousDs, envCode.getValue());
                    DynamicDataSourceContextHolder.push(envCode.getValue());
                }
                // 执行目标方法
                return joinPoint.proceed();
            } finally {
                // 清理数据源(恢复到默认数据源)
                if (envCode != null) {
                    DynamicDataSourceContextHolder.clear();
                }
            }
        }
        /**
         * 通过反射获取方法参数
         * 自动识别DataSourceEnum类型的参数
         * 支持任意位置的DataSourceEnum参数
         *
         * @param joinPoint 切点
         * @return DataSourceEnum参数
         */
        private DataSourceEnum extractEnvCode(ProceedingJoinPoint joinPoint) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Parameter[] parameters = signature.getMethod().getParameters();
            // 遍历参数查找EnvCode类型的参数
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].getType().isAssignableFrom(DataSourceEnum.class)) {
                    Object arg = joinPoint.getArgs()[i];
                    if (arg instanceof DataSourceEnum) {
                        return (DataSourceEnum) arg;
                    }
                }
            }
            return null;
        }
    }

    6、日志输出效果

    MyBATis-Plus 我们使用 org.apache.ibatis.logging.slf4j.Slf4jImpl 日志实现,配置对应的日志级别

    mybatis-plus:
      mapper-locations: classpath:mapper/*.XML
      configuration:
        log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    logging:
      level:
        root: info
        # 注意,logback不支持**匹配
        com.github.wxhnyfy.**.mapper: debug
        druid.sql.Statement: debug

    日志输出效果如下图

    SpringBoot + Druid + Dynamic Datasource 多数据源配置方案

    到此这篇关于SpringBoot + Druid + Dynamic Datasource 多数据源配置的文章就介绍到这了,更多相关SpringBoot Druid Dynamic Datasource 多数据源内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号