Spring Boot + 多数据源 + Druid:监控页面 + 控制台 SQL 日志的完整实践

大家好,我是服务端技术精选的作者。今天咱们聊聊一个在企业级开发中非常常见的需求:多数据源管理。

多数据源的挑战

在我们的日常开发工作中,经常会遇到这样的场景:

  • 需要连接多个数据库,可能是不同的业务系统
  • 要实现读写分离,提升数据库性能
  • 需要连接不同类型的数据库(MySQL、Oracle、PostgreSQL等)
  • 对数据库连接进行统一监控和管理

传统的单数据源配置显然无法满足这些复杂需求。今天我们就来聊聊如何用Spring Boot + Druid构建一个功能完善的多数据源管理系统。

解决方案思路

今天我们要解决的,就是如何构建一个多数据源管理平台,包含监控页面和SQL日志功能。

核心思路是:

  1. 多数据源配置:动态切换不同数据源
  2. Druid监控:提供数据库连接池监控
  3. SQL日志输出:记录详细的SQL执行信息
  4. 统一管理界面:集中查看和管理

多数据源配置实现

1. 数据源配置

首先,我们需要配置多个数据源:

spring:
  datasource:
    # 主数据源
    primary:
      url: jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=utf8
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 从数据源  
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=utf8
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver

# Druid连接池配置
druid:
  initial-size: 5
  min-idle: 5
  max-active: 20
  max-wait: 60000
  time-between-eviction-runs-millis: 60000
  min-evictable-idle-time-millis: 300000
  validation-query: SELECT 1
  test-while-idle: true
  test-on-borrow: false
  test-on-return: false
  filters: stat,wall,log4j

2. 动态数据源路由

实现数据源的动态切换:

public class DynamicDataSource extends AbstractRoutingDataSource {
    
    private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();
    
    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceHolder.get();
    }
    
    public static void setDataSource(String dataSource) {
        dataSourceHolder.set(dataSource);
    }
    
    public static void clearDataSource() {
        dataSourceHolder.remove();
    }
}

3. 数据源配置类

@Configuration
@Primary
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DruidDataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties("spring.datasource.secondary") 
    public DataSource secondaryDataSource() {
        return DruidDataSourceBuilder.create().build();
    }
    
    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("primary", primaryDataSource());
        dataSourceMap.put("secondary", secondaryDataSource());
        
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
        
        return dynamicDataSource;
    }
}

Druid监控配置

1. 监控配置

@Configuration
public class DruidConfig {
    
    @Bean
    public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
        ServletRegistrationBean<StatViewServlet> registrationBean = 
            new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        
        // IP白名单
        registrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名单
        registrationBean.addInitParameter("deny", "");
        // 登录用户名密码
        registrationBean.addInitParameter("loginUsername", "admin");
        registrationBean.addInitParameter("loginPassword", "password");
        // 是否可以重置数据
        registrationBean.addInitParameter("resetEnable", "false");
        
        return registrationBean;
    }
    
    @Bean
    public FilterRegistrationBean<WebStatFilter> druidStatFilter() {
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = 
            new FilterRegistrationBean<>(new WebStatFilter());
        
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        
        return filterRegistrationBean;
    }
}

2. SQL拦截器配置

@Configuration
public class MyBatisConfig {
    
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDataSource());
        
        // 添加SQL拦截器
        Interceptor[] interceptors = new Interceptor[]{
            new PerformanceInterceptor(),
            new PaginationInnerInterceptor(DbType.MYSQL)
        };
        factoryBean.setPlugins(interceptors);
        
        return factoryBean.getObject();
    }
}

SQL日志输出

1. 配置SQL日志

logging:
  level:
    # 显示SQL
    com.baomidou.mybatisplus.core: debug
    # 显示SQL参数
    com.alibaba.druid: debug
    # 显示具体Mapper的SQL
    com.example.mapper: debug

mybatis-plus:
  configuration:
    # 开启SQL日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 打印参数
    log-params: true

2. 自定义SQL拦截器

@Component
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlLogInterceptor implements Interceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlLogInterceptor.class);
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler, 
            SystemMetaObject.DEFAULT_OBJECT_FACTORY, 
            SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, 
            new DefaultReflectorFactory());
        
        String sql = statementHandler.getBoundSql().getSql();
        Object parameterObject = statementHandler.getBoundSql().getParameterObject();
        
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        long endTime = System.currentTimeMillis();
        
        logger.info("SQL执行耗时: {} ms, SQL: {}, 参数: {}", 
            endTime - startTime, sql.trim(), parameterObject);
        
        return result;
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {}
}

数据源切换注解

1. 自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
    String value() default "primary";
}

2. AOP切面实现

@Aspect
@Component
public class DataSourceAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    
    @Around("@annotation(targetDataSource)")
    public Object around(ProceedingJoinPoint point, TargetDataSource targetDataSource) throws Throwable {
        String dataSource = targetDataSource.value();
        logger.info("切换到数据源: {}", dataSource);
        
        DynamicDataSource.setDataSource(dataSource);
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.info("数据源已还原");
        }
    }
}

实际应用示例

1. Service层使用

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @TargetDataSource("primary")
    public List<User> getUsersFromPrimary() {
        return userMapper.selectAll();
    }
    
    @TargetDataSource("secondary") 
    public List<User> getUsersFromSecondary() {
        return userMapper.selectAll();
    }
}

2. 监控页面访问

启动应用后,可以通过以下地址访问Druid监控页面:

性能优化建议

1. 连接池参数调优

druid:
  # 初始化连接数
  initial-size: 10
  # 最小空闲连接数
  min-idle: 10  
  # 最大连接数
  max-active: 50
  # 获取连接等待超时时间
  max-wait: 60000
  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接
  time-between-eviction-runs-millis: 60000
  # 配置一个连接在池中最小生存的时间
  min-evictable-idle-time-millis: 300000
  # 验证连接有效性
  validation-query: SELECT 1
  test-while-idle: true
  test-on-borrow: false
  test-on-return: false

2. SQL性能监控

通过Druid的SQL监控功能,可以:

  • 识别慢查询SQL
  • 分析SQL执行频率
  • 监控数据库连接使用情况
  • 发现潜在的性能问题

注意事项

在使用多数据源时,需要注意以下几点:

  1. 事务管理:跨数据源事务需要使用分布式事务
  2. 连接泄漏:确保及时释放数据库连接
  3. 配置管理:敏感信息如密码应加密存储
  4. 监控告警:设置数据库连接池的告警阈值

总结

通过Spring Boot + 多数据源 + Druid的组合,我们可以构建一个功能完善、易于监控的数据库访问系统。这种架构不仅解决了多数据源访问的问题,还提供了强大的监控和日志功能,大大提升了开发和运维效率。

希望这篇文章对你有所帮助!如果你觉得有用,欢迎关注【服务端技术精选】公众号,获取更多后端技术干货。


原文首发于 www.jiangyi.space

转载请注明出处


标题:Spring Boot + 多数据源 + Druid:监控页面 + 控制台 SQL 日志的完整实践
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/01/17/1768638410238.html

    0 评论
avatar