当前位置: 首页 > news >正文

Spring Boot整合MyBatis-Plus与Druid:构建高效多数据源方案

1. 项目概述与核心价值

最近在重构一个老的后台管理系统,数据源这块遇到了瓶颈。原来的单数据源配置,在对接新的报表库和日志库时显得捉襟见肘,频繁的手动切换数据源不仅代码冗余,还容易出错。同时,随着用户量上来,偶尔出现的数据库连接泄露和性能瓶颈也让人头疼。于是,我决定对数据访问层进行一次彻底的升级,核心目标就三个:用 MyBatis-Plus 提升开发效率,用 Druid 连接池保障稳定与可观测性,最后也是最关键的,实现一套优雅、易维护的多数据源方案。

这个组合拳打下来,效果立竿见影。MyBatis-Plus 的通用 Mapper 和条件构造器让 CRUD 代码量减少了70%以上;Druid 强大的监控功能让我第一次清晰地看到了 SQL 执行状况和连接池的健康度;而多数据源配置,则让业务代码与具体数据库彻底解耦,增删数据源就像改个配置一样简单。这套方案几乎成了我后续所有 Spring Boot 项目的标准模板,无论是简单的单体应用,还是需要分库分表的复杂场景,都能很好地胜任。如果你也在为数据访问层的效率、稳定性和扩展性发愁,那么这篇从实战中踩坑总结出来的整合指南,应该能给你提供一条清晰的路径。

2. 技术选型与依赖配置解析

2.1 为什么是 MyBatis-Plus + Druid?

在 Java 持久层框架的选择上,MyBatis 因其灵活性和对复杂 SQL 的良好控制而备受青睐。但原生 MyBatis 需要大量模板代码,比如每个实体都要写基础的 XML 映射或注解 SQL。MyBatis-Plus(简称 MP)在完全兼容 MyBatis 所有特性的基础上,提供了强大的 CRUD 增强功能。它的通用 Mapper让你无需编写任何 SQL 或 XML,就能完成单表操作;条件构造器(Wrapper)可以用 Lambda 表达式安全、直观地构建复杂查询条件;还有分页插件、代码生成器等一系列开箱即用的功能,能极大提升开发效率。对于大多数业务场景,80%的操作都是单表 CRUD,MP 能帮你省下大量时间。

而数据库连接池,我们选择了阿里巴巴开源的 Druid。市面上常见的连接池有 HikariCP、Tomcat JDBC、DBCP2 等,HikariCP 以性能卓越著称。那为什么选 Druid?因为 Druid 在性能与功能监控之间取得了非常好的平衡。它不仅提供了高效的连接池管理,更内置了强大的监控功能。通过一个简单的 Web 页面,你就能实时查看:当前活跃连接数、等待线程数、SQL 执行次数、最慢的 SQL 列表、事务启闭情况等。这对于生产环境的运维和性能调优至关重要。当系统出现数据库瓶颈时,Druid 的监控面板往往是第一个需要查看的地方。此外,Druid 还支持密码加密、SQL 防火墙、防御 SQL 注入等安全特性,功能非常全面。

2.2 依赖版本管理与冲突规避

版本兼容性是整合的第一步,也是最容易踩坑的地方。根据当前(以撰写时为准)的主流版本,我推荐以下组合,这也是经过多个生产项目验证的稳定搭配:

  • Spring Boot: 2.7.x (长期支持版本,生态稳定)
  • MyBatis-Plus: 3.5.3.1
  • Druid: 1.2.16

你提到的网络热词中 “mybatis-plus version3.5.17对应的springboot版本”, 实际上,MyBatis-Plus 3.5.x 版本对 Spring Boot 的兼容性很好,通常支持 Spring Boot 2.5.x 到 3.x。但为了追求极致的稳定性,我建议参考官方文档或 Maven 仓库的依赖关系。一个简单的检查方法是查看mybatis-plus-boot-starter这个依赖内部引用的spring-boot-starter-*版本。

pom.xml中,依赖配置如下。特别注意:我们引入的是mybatis-plus-boot-starter,它会自动引入 MyBatis 和 MyBatis-Spring 的适配依赖,无需单独引入。同时,我们排除了 Spring Boot 默认的 HikariCP 连接池,确保使用 Druid。

<dependencies> <!-- Spring Boot Web 基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus 启动器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Druid 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> <!-- Lombok (可选,用于简化实体类) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

注意:使用druid-spring-boot-starter而非单纯的druid依赖,前者提供了与 Spring Boot 配置属性(application.yml)的无缝集成,配置起来更加方便。

3. 单数据源标准配置详解

在搭建多数据源之前,我们必须先把单数据源的配置吃透、配稳。这是整个数据访问层的基石,配置不当会直接导致性能问题或连接泄露。

3.1 基础 YAML 配置与参数解读

我们将配置写在application.yml中。Druid 的配置参数非常多,但核心的也就十几项。下面是一个兼顾性能和功能的推荐配置:

spring: datasource: # 使用 Druid 数据源 type: com.alibaba.druid.pool.DruidDataSource # Druid 连接池专属配置 druid: # 基本连接参数 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/your_primary_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 # 连接池大小配置(核心参数) initial-size: 5 # 初始化连接数 min-idle: 5 # 最小空闲连接数 max-active: 20 # 最大活跃连接数 max-wait: 60000 # 获取连接时最大等待时间(毫秒) # 连接有效性检测配置 validation-query: SELECT 1 test-while-idle: true # 空闲时检测连接是否有效 test-on-borrow: false # 借出连接时不测试(影响性能,依赖空闲检测即可) test-on-return: false # 归还连接时不测试 time-between-eviction-runs-millis: 60000 # 空闲连接检测线程运行间隔(毫秒) min-evictable-idle-time-millis: 300000 # 连接在池中最小生存时间(毫秒) # 监控配置 stat-view-servlet: enabled: true # 启用 StatViewServlet,提供监控页面 login-username: admin # 监控页面登录用户名 login-password: admin # 监控页面登录密码 allow: 127.0.0.1 # 允许访问的IP,生产环境务必设置 deny: # 拒绝访问的IP reset-enable: false # 禁用重置监控数据功能 web-stat-filter: enabled: true # 启用 WebStatFilter,监控 Web 关联 url-pattern: /* # 过滤所有URL exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" # 排除静态资源和监控本身 filter: stat: enabled: true # 启用 StatFilter,用于统计 SQL 执行性能 log-slow-sql: true # 记录慢 SQL slow-sql-millis: 2000 # 慢 SQL 阈值(毫秒),超过此时间则记录 merge-sql: true # 合并相似的 SQL 统计 wall: enabled: true # 启用 WallFilter,防御 SQL 注入 config: multi-statement-allow: true # 允许一次执行多条SQL(根据业务需要)

关键参数解读与调优建议:

  1. 连接池大小 (initial-size,min-idle,max-active): 这是调优重点。initial-sizemin-idle设置过小,应用启动或突发流量时,频繁创建连接会造成延迟;设置过大,则浪费资源。max-active是硬限制,超过后新的请求会等待(max-wait)。一个经验公式:对于常规的 Web 应用,max-active可以设置为(核心线程数 * 2) + 磁盘 spindle 数。例如,你的 Tomcat 线程池设置为 200,那么max-active设为 40-50 是个不错的起点,再根据监控调整。
  2. 连接检测 (test-while-idle): 务必开启。数据库服务端可能会因为超时、重启等原因断开空闲连接。开启此选项后,Druid 的后台线程会定期检测空闲连接的有效性,将无效连接剔除,保证从池中取出的连接都是可用的。test-on-borrow建议关闭,因为它会在每次获取连接时都执行检测,对性能有影响。
  3. 监控配置 (stat-view-servlet):allowdeny在生产环境必须配置,否则监控页面可能暴露给公网,造成安全风险。login-usernamelogin-password也要设置强密码。

3.2 MyBatis-Plus 基础配置与代码生成

配置好数据源后,接下来配置 MyBatis-Plus。我们通常需要一个配置类MybatisPlusConfig

@Configuration @MapperScan("com.yourpackage.mapper") // 指定 Mapper 接口的扫描路径 public class MybatisPlusConfig { /** * 分页插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } /** * 元数据填充插件(自动填充 createTime, updateTime 等字段) */ @Bean public MetaObjectHandler metaObjectHandler() { return new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }; } }

对于实体类,我们使用 Lombok 简化代码,并加上 MyBatis-Plus 的注解:

@Data // Lombok 注解,生成 getter/setter/toString 等 @TableName("sys_user") // 指定表名,若类名与表名一致(忽略大小写)可省略 public class User { @TableId(type = IdType.AUTO) // 主键,自增 private Long id; private String username; private String email; @TableField(fill = FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; }

对应的 Mapper 接口极其简单,只需继承BaseMapper

public interface UserMapper extends BaseMapper<User> { // 无需定义任何方法,基本的 CRUD 已由 BaseMapper 提供 // 你可以在此定义自定义的复杂 SQL 方法 }

代码生成器实战:对于已有数据库表,手动编写实体和 Mapper 很繁琐。MyBatis-Plus 提供了强大的代码生成器(com.baomidou.mybatisplus.generator)。你需要编写一个简单的生成类,配置好数据源、包路径、策略(表前缀、字段命名等),运行即可一键生成 Entity、Mapper、XML、Service、Controller 全套代码。这是提升初期开发效率的神器,网上有很多现成的模板,这里不展开,但其核心思想是“配置化生成”,避免重复劳动。

4. 多数据源动态切换方案实现

单数据源配置是基础,而多数据源则是应对复杂业务场景的必备能力。常见的场景有:主从读写分离、业务分库(用户库、订单库、日志库分离)、对接不同数据库产品(MySQL + PostgreSQL)等。Spring Boot 下实现多数据源,核心是动态路由,即根据当前执行的上下文,决定使用哪个数据源。

4.1 抽象与设计:数据源路由原理

Spring 的AbstractRoutingDataSource是这个方案的核心。它是一个抽象类,内部维护了一个Map<Object, DataSource>用来存放多个目标数据源,并提供了一个determineCurrentLookupKey()的抽象方法。我们的任务就是:

  1. 继承这个类,实现determineCurrentLookupKey()方法,让它返回一个“数据源标识键”。
  2. 用一个工具类(如DataSourceContextHolder)来存储这个“标识键”,通常使用ThreadLocal,因为它的生命周期与当前线程绑定,可以保证在多线程环境下,每个线程的数据源选择是独立的、正确的。
  3. 在需要切换数据源的地方(如 Service 方法上通过自定义注解),调用工具类来设置当前线程的“标识键”。
  4. AbstractRoutingDataSource在需要获取连接时,会调用我们实现的determineCurrentLookupKey()拿到标识键,然后从 Map 中找到对应的真实DataSource返回。

4.2 完整实现步骤与代码

我们以实现一个主数据源 (primary) 和一个从数据源 (secondary) 为例。

第一步:定义数据源标识枚举和上下文持有器

public class DataSourceType { public static final String PRIMARY = "primary"; public static final String SECONDARY = "secondary"; } public class DynamicDataSourceContextHolder { // 使用 ThreadLocal 保证线程安全 private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSourceType(String dsType) { CONTEXT_HOLDER.set(dsType); } public static String getDataSourceType() { return CONTEXT_HOLDER.get(); } public static void clearDataSourceType() { CONTEXT_HOLDER.remove(); } }

第二步:配置多个真实数据源

application.yml中,我们不再使用spring.datasource的默认配置,而是为每个数据源单独定义属性。

# 多数据源配置 datasource: primary: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 min-idle: 5 max-active: 20 # ... 其他 Druid 配置同单数据源示例 secondary: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 3 min-idle: 3 max-active: 15 # ... 其他 Druid 配置

第三步:创建数据源配置类

这个类负责读取 YAML 配置,创建出两个 Druid 数据源 Bean,并将它们注册到动态数据源中。

@Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "datasource.primary") public DataSource primaryDataSource() { // 这里会利用 druid-spring-boot-starter 自动绑定配置到 DruidDataSource return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "datasource.secondary") public DataSource secondaryDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary // 标记为主 Bean,Spring 在注入 DataSource 时优先使用这个 public DynamicDataSource dataSource(DataSource primaryDataSource, DataSource secondaryDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(2); targetDataSources.put(DataSourceType.PRIMARY, primaryDataSource); targetDataSources.put(DataSourceType.SECONDARY, secondaryDataSource); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(primaryDataSource); // 设置默认数据源 dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } }

第四步:实现动态数据源路由类

public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 从 ThreadLocal 中获取当前线程指定的数据源键 return DynamicDataSourceContextHolder.getDataSourceType(); } }

第五步:创建自定义注解和 AOP 切面

为了方便使用,我们创建一个@DataSource注解,可以标注在 Service 类或方法上。

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String value() default DataSourceType.PRIMARY; }

然后,通过 AOP 切面,在方法执行前根据注解切换数据源,执行后清理。

@Aspect @Component @Order(-1) // 确保在事务切面之前执行 public class DataSourceAspect { @Around("@annotation(dataSource) || @within(dataSource)") public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable { String dsKey = dataSource.value(); boolean clearFlag = false; // 如果当前线程没有设置数据源,则进行设置 if (DynamicDataSourceContextHolder.getDataSourceType() == null) { DynamicDataSourceContextHolder.setDataSourceType(dsKey); clearFlag = true; } try { return point.proceed(); } finally { // 如果是我们设置的,则在方法执行完毕后清理 if (clearFlag) { DynamicDataSourceContextHolder.clearDataSourceType(); } } } }

第六步:在 MyBatis-Plus 配置中指定数据源

修改之前的MybatisPlusConfig,确保SqlSessionFactory使用的是我们定义的动态数据源。

@Configuration @MapperScan("com.yourpackage.mapper") public class MybatisPlusConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); // 注入动态数据源 // 其他配置,如分页插件、全局配置等 MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); sessionFactory.setPlugins(interceptor); return sessionFactory.getObject(); } // ... 其他 Bean 配置 }

第七步:在业务层使用

现在,你就可以在 Service 层灵活地使用多数据源了。

@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; // 这个 Mapper 默认使用 primary 数据源 @Override @DataSource(DataSourceType.PRIMARY) // 显式指定,可省略(因为默认就是 PRIMARY) public User getUserFromPrimary(Long id) { return userMapper.selectById(id); } @Override @DataSource(DataSourceType.SECONDARY) // 切换到 secondary 数据源 public User getUserFromSecondary(Long id) { return userMapper.selectById(id); // 同一个 Mapper,但数据源已切换 } }

重要提示:多数据源与事务管理器(@Transactional)结合使用时需要格外小心。默认的 Spring 事务管理器只绑定一个数据源。在多数据源场景下,你需要为每个数据源配置独立的事务管理器,并使用如@Transactional(value = “primaryTransactionManager”)来指定。或者,可以考虑使用如 JTA 等分布式事务方案,但这会引入复杂性。对于大多数“最终一致性”可接受的业务场景,更常见的做法是避免在跨数据源的操作上使用单一事务,或者将跨库操作设计为补偿性事务。

5. Druid 监控与性能调优实战

配置好是第一步,用得好才是关键。Druid 的监控面板是我们洞察数据库访问状态的“眼睛”。

5.1 监控面板访问与核心指标解读

启动应用后,访问http://你的IP:端口/druid/index.html,输入配置的用户名密码,即可进入监控首页。

你需要重点关注以下几个面板:

  1. 数据源 (DataSource):这里展示了连接池的实时状态。

    • 活跃连接数 (ActiveCount):正在被使用的连接数。如果这个数长期接近max-active,说明连接池大小可能不足,需要考虑调大或优化慢 SQL。
    • 等待线程数 (WaitThreadCount):在等待获取连接的线程数。如果这个数大于0,说明有线程在排队,max-wait时间可能被触发,是性能瓶颈的明显信号。
    • 池中连接数 (PoolingCount):当前连接池中总的连接数(包括空闲和活跃)。它应该在min-idlemax-active之间波动。
  2. SQL 监控 (SQL Monitor):这是最有价值的面板之一。

    • 你可以看到所有执行过的 SQL,以及它们的执行次数、总耗时、最慢执行时间、平均执行时间等。
    • 重点关注“执行最慢”的 SQL。这里列出的就是超过你配置的slow-sql-millis阈值的慢查询。优化这些 SQL 是提升系统性能最直接有效的手段。
    • “执行+RsHold”时间分布(即网络热词中的druid stat的执行时间分布):这个统计图非常直观。它展示了 SQL 执行时间(Execution)和结果集持有时间(ResultSet Hold)的分布。理想情况下,大部分 SQL 都应在最左边的短时间区间内。如果长时间区间(如>100ms, >1s)的柱状图很高,说明存在大量慢查询或结果集处理过慢的问题。
  3. Web 应用 (Web App)/URI 监控 (URI Monitor):这可以帮助你定位是哪个 Controller 接口或页面请求导致了密集的数据库访问,结合 SQL 监控,可以精准定位性能热点。

5.2 连接池参数调优与问题排查

根据监控数据,我们可以动态调整连接池参数。这里有几个常见的调优场景:

  • 场景一:频繁出现Connection is not available, request timed out after ...错误。

    • 排查:查看 Druid 监控的“等待线程数”和“活跃连接数”。如果等待线程数持续很高,且活跃连接数达到max-active,说明并发请求超过了连接池的处理能力。
    • 解决
      1. 优先优化 SQL:检查 SQL 监控中的慢查询,为频繁查询且耗时的 SQL 添加合适的索引。
      2. 调整池大小:如果 SQL 已优化,可适当增加max-active(如从 20 调到 30或50)。但注意,连接数不是越多越好,数据库服务器也有连接数上限和资源消耗。
      3. 检查连接泄露:在“连接泄露检测”面板,查看是否有连接长时间未关闭。这通常是由于代码中没有正确关闭ConnectionStatementResultSet导致的。MyBatis-Plus 通常能很好地管理这些资源,但如果你在代码中手动获取了连接,务必在 finally 块中关闭。
  • 场景二:应用启动一段时间后,偶尔出现连接超时或通信链路失败。

    • 排查:这很可能是数据库服务端断开了空闲连接(例如,MySQL 的wait_timeout默认为 8 小时),而客户端连接池不知道,仍然持有无效连接。
    • 解决:确保 Druid 的空闲连接检测配置正确且生效。
      spring: datasource: druid: test-while-idle: true # 必须为 true validation-query: SELECT 1 # 简单的验证查询 time-between-eviction-runs-millis: 60000 # 检测间隔,建议 1分钟 min-evictable-idle-time-millis: 300000 # 最小空闲时间,建议 5分钟
      这样配置后,Druid 会每隔time-between-eviction-runs-millis检查一次空闲时间超过min-evictable-idle-time-millis的连接,并用validation-query测试其有效性,无效则丢弃。
  • 场景三:监控中发现大量相似的 SQL,只是参数不同。

    • 解决:开启filter.stat.merge-sql: true。这个功能会将执行模式相同(仅参数不同)的 SQL 合并统计,使得监控页面更加清晰,便于你分析哪类 SQL 是执行大头。例如,SELECT * FROM user WHERE id = 1SELECT * FROM user WHERE id = 2会被合并统计为SELECT * FROM user WHERE id = ?

6. 生产环境进阶配置与避坑指南

将这套方案部署到生产环境,还需要考虑更多因素。

6.1 敏感信息加密与配置文件分离

application.yml中明文存储数据库密码是极不安全的。Druid 提供了密码加密功能。

  1. 生成加密密码:使用 Druid 自带的com.alibaba.druid.filter.config.ConfigTools类来加密你的密码。
    java -cp druid-1.2.16.jar com.alibaba.druid.filter.config.ConfigTools your_db_password
    你会得到一对公钥(publicKey)和加密后的密码(password)。
  2. 修改配置
    spring: datasource: druid: connection-properties: config.decrypt=true;config.decrypt.key=${PUBLIC_KEY} password: ${ENCRYPTED_PASSWORD} filter: config: enabled: true
    ${PUBLIC_KEY}${ENCRYPTED_PASSWORD}替换为生成的值。更安全的做法是将这些敏感信息放入环境变量或专门的配置中心(如 Apollo, Nacos)。

6.2 多数据源下的 MyBatis-Plus 注意事项

  1. Mapper 接口与数据源绑定:在上面的动态数据源方案中,Mapper 接口本身并不绑定特定数据源,数据源的选择由 AOP 切面在 Service 层决定。这意味着同一个 Mapper 方法,在不同的 Service 方法调用中,可能访问不同的数据库。这非常灵活,但要求开发人员对@DataSource注解的使用有清晰的规划,避免混乱。
  2. 分页插件:我们已经在配置类中全局配置了分页插件。它在多数据源下同样工作,因为PaginationInnerInterceptor是通过解析 SQL 语句来添加分页语句的,不依赖具体的数据源。但请注意,如果你的多个数据源是不同类型的数据库(如 MySQL 和 PostgreSQL),分页语法不同,你需要为不同的数据源配置不同的PaginationInnerInterceptor,这需要更复杂的动态配置。
  3. 事务管理:这是多数据源最大的挑战。@Transactional注解默认使用一个PlatformTransactionManager。在多数据源下,你需要定义多个对应不同数据源的DataSourceTransactionManagerBean。然后,在需要事务的 Service 方法上,使用@Transactional(transactionManager = “primaryTransactionManager”)来指定。跨数据源的事务(分布式事务)超出了本文范围,通常需要引入 Seata 等框架,或者从业务设计上避免这种强一致性需求。

6.3 常见问题排查清单

这里整理了一份快速排查表,当你遇到问题时可以按图索骥:

问题现象可能原因排查步骤与解决方案
启动报错:Failed to configure a DataSource1. 数据库连接信息错误。
2. 依赖缺失(如 MySQL 驱动)。
3. 多数据源配置冲突。
1. 检查url,username,password
2. 确认pom.xml中有数据库驱动依赖。
3. 检查是否同时存在spring.datasource和多数据源自定义配置,移除冲突项。
监控页面/druid无法访问1. 未启用stat-view-servlet
2. IP 地址被allow/deny限制。
3. 项目路径(context-path)影响。
1. 确认配置enabled: true
2. 检查allow配置,本地可暂时设为allow:(空,允许所有)测试。
3. 访问路径应为http://host:port/{context-path}/druid/index.html
报错:Could not get JDBC Connection1. 连接池耗尽 (max-active太小)。
2. 连接泄露(未正确关闭)。
3. 数据库服务异常或网络不通。
1. 查看 Druid 监控“活跃连接数”和“等待线程数”,调大max-active
2. 开启 Druid 的连接泄露检测日志。
3. 使用数据库客户端工具测试网络连通性。
多数据源切换不生效1.@DataSource注解未生效(AOP 顺序问题)。
2.ThreadLocal数据未清理,导致污染后续请求。
1. 确保切面类被 Spring 扫描到,并设置@Order(-1)使其先于事务切面执行。
2. 在 AOP 切面的finally块中务必调用clearDataSourceType()
SQL 执行特别慢1. 数据库表缺乏索引。
2. SQL 写法有问题(如SELECT *, 不当的 JOIN)。
3. 网络延迟高。
1. 在 Druid SQL 监控中找到慢 SQL,在数据库中使用EXPLAIN分析其执行计划。
2. 优化 SQL,只查询需要的字段,添加合适的索引。
3. 检查应用与数据库服务器的网络状况。

这套 Spring Boot + MyBatis-Plus + Druid + 多数据源的组合,经过多个项目的锤炼,已经非常稳定可靠。关键在于理解每个组件的作用原理,并根据自己项目的实际监控数据进行细致的调优。从单数据源到多数据源的演进,不仅是技术的升级,更是对系统架构清晰度的一种追求。希望这篇长文能帮你少走弯路,顺利搭建出高效、健壮的数据访问层。

http://www.jsqmd.com/news/1384491/

相关文章:

  • STM32到GD32的RT-Thread迁移实战:Pin to Pin替换的避坑指南
  • 保姆级教程|银行流水翻译公证要在哪办理?多久可以出证? - 实用干货补给站
  • 蜂小推邀请码是多少?**直达37554040,附网盘拉新玩法指南 - 甄选测评官
  • glgeim文件解析:从来源排查到处理方案的完整指南
  • 2026年8月沈阳别墅毛坯全案整装公司口碑好:别墅原创家装工艺林凤装饰 - GrowthUME
  • 学 AI3D 人工智能,把握数字新赛道|2026 专业招生简章 - 武汉学历升学规划
  • 2026年沈阳会计代账挑选攻略 雪球财税等机构梳理 - 小范同学a
  • 靠谱的工业品阿里代运营公司? - GrowthUME
  • 发那科机器人程序导出到U盘:完整流程、关键设置与深度排错指南
  • 2026 年 Python 数据分析全栈实战!从 Pandas 到 PySpark,可视化到 AI
  • 企业考试系统如何对接OA、钉钉和企业微信?SSO单点登录、组织同步与权限一致性设计
  • 第91讲:接单提速——一半时间试错,一半时间交付量产代码
  • 2026年长春玻璃钢雕塑厂家推荐名单汇总一览 - 起跑123
  • 河源紫金黄金回收正规渠道实测:紫金源奢汇资质与服务全维度测评 - 紫金的金
  • 2026干线工程高稳定性光纤熔接机品牌盘点:进口与国产各有哪些值得关注
  • 2026机器人3D视觉系统品牌选型指南-国内外头部品牌全面解析 - 资讯综合
  • 2026成都平价靠谱装修公司大盘点:正规合规、实力口碑兼备的服务商筛选攻略及签约避坑全指南 - U渠道
  • 9款论文辅助工具实测:从文献综述到格式优化全流程指南
  • Java设计原则:SOLID与七大核心原则实践指南
  • 选橡胶硫化仪时,厂家、品牌、售后这几方面怎么权衡? - 品牌推荐大师1
  • 逻辑学基础:性质命题与模态命题的HAIMIAN模型全解与实战应用
  • PHP开发实战:从环境配置、代码调试到安全部署的完整解决方案
  • 营销岗位简历问答系统设计与应用
  • Linux企业级权限管理:SELinux与AppArmor实战指南
  • Spark Streaming实战:从微批处理到生产级应用的性能调优与容错设计
  • Win10 LTSC纯净系统安装与优化全指南:从镜像获取到终极配置
  • 2026成都好装修不贵的正规整装服务商盘点:选型标准、实力评估、避坑指南+签约FAQ详解 - 行业观察网
  • 儿童摄影样片研发 - 甄选测评官
  • 如何不安装软件就能偷看Windows安装包的小秘密?lessmsi让你成为MSI文件侦探
  • 三亚到北京私家车托运需要什么手续 常见问题解答 - 全域品牌推荐