ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot + MyBatis-Plus多数据源整合:Druid监控、SQL过滤与加密实践

Spring Boot + MyBatis-Plus多数据源整合:Druid监控、SQL过滤与加密实践 接手过一个需要拆库的项目数据库从一套拆成了三套主库管交易、从库扛查询、还有一个老报表库单独跑定时任务。MyBatis-Plus 本身挺好用但多数据源切换、Druid 连接池、SQL 过滤和监控这几件事一股脑涌过来的时候还是需要认真理一理。这篇就把我实际踩过坑、验证过可行的一套组合方案写透用 dynamic-datasource 做多数据源切换用 Druid 做连接池和 SQL 过滤把监控和密码加密也一并配好同时覆盖 MyBatis-Plus XML 与 Mapper 同目录打包、JPA 与 MyBatis-Plus 选型这类经常被问到的延伸问题。这篇文章适合 Spring Boot 项目里已经遇到过“数据源切不过去”“Druid 监控打不开”“SQL 被墙了不知道怎么回事”的开发者也适合正准备从单数据源升级到多数据源的团队。1. 为什么第一反应是 dynamic-datasource 而不是手写路由1.1 多数据源项目的真实画像先说清楚“多数据源”到底解决什么场景。最常见的无非三种读写分离主库负责写、从库负责读减少主库压力业务分库比如订单库、用户库、日志库各占一台实例一个服务要同时操作多个库还有报表或数仓库跟业务库隔离避免大查询把在线业务拖垮。这三种场景背后都有一个共同诉求同一个服务进程里根据业务代码的调用位置决定这次数据库操作走哪个库而且切换不能影响到事务一致性和连接复用。很多团队一开始会想多数据源不就是多配置几个 DataSource 吗Spring 里本来就支持为每个数据源配置一套 SqlSessionFactory 或 JpaTransactionManager。可真到了代码里就会发现如果每换一个库就要注入一套 SqlSessionTemplate业务代码里到处都是 factoryA、factoryB 的引用维护成本会直线上升。更麻烦的是一旦涉及嵌套事务、AOP 切面和连接池复用纯手工路由很容易在细节上翻车。1.2 几种实现路径的对比我简单梳理过三条常见路线各有各的适用面方案核心思路优点缺点手写 AbstractRoutingDataSource 自定义注解用 Spring 提供的动态路由 DataSource在 DAO 层根据上下文切换 key不引入额外依赖代码透明路由状态容易串事务和线程池场景下要自己管ShardingSphere 数据分片把多库抽象成分片规则按分片键路由适合分库分表规模较大的场景能力强配置复杂度高对已有 SQL 有约束dynamic-datasource-spring-boot-starter基于 AOP 上下文持有者切换数据源注解 DS 声明式路由语义清晰、与 MyBatis-Plus 配合成熟、事务支持完善需要接受框架约定的规则手写路由最头疼的点是在高并发下如何保证“这次请求的路由状态”不泄漏到下一个请求。Spring 的 AbstractRoutingDataSource 本身只是提供了 determineCurrentLookupKey 这个口子关键在于你的上下文怎么存、怎么清理。很多人会直接用 ThreadLocal但用了线程池之后如果 Web 请求线程处理完没有被正确清理下一次复用同一个线程就会跑到上一个请求指定的数据源上这种脏数据问题特别难排查。dynamic-datasource 的做法是把路由状态放到一个被包装过的上下文中并且会在每次执行后清理这个细节比大多数团队自己写的版本要稳。1.3 与 MyBatis-Plus 的配合点MyBatis-Plus 本身并不负责数据源选择它只是把 MyBatis 的 Mapper 接口、SqlSession 和 SQL 执行过程包装得更“业务化”。数据源切换发生在 MyBatis 获取连接之前所以 dynamic-datasource 的 AOP 切面会先把当前数据源 key 放进上下文然后再进入 MyBatis 执行链路两者没有冲突。这也意味着 MyBatis-Plus 的分页插件、乐观锁插件、字段自动填充插件都能照常工作因为它们是在 SqlSession 层做拦截不需要关心连接来自哪个 DataSource。真正要注意的只是事务边界的顺序问题后面第 5 章我会重点展开。选型时优先考虑 dynamic-datasource而不是自己写路由本质上就是想把“切换数据源”这个横切关注点交给一个经过验证的框架来处理让业务代码保持干净。2. 一步步把多数据源和 Druid 接进来2.1 依赖引入与版本对应先把 Maven 依赖放出来。我这里用的是 Spring Boot 2.7 组合MyBatis-Plus 3.5.x、dynamic-datasource 3.5.2、Druid 1.2.20这是目前生产环境里验证比较充分的版本组合。dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version3.5.2/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.20/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency注意一点dynamic-datasource 在 3.5.2 版本往上已经对 Spring Boot 3 有单独的 startergroupId 一样artifactId 会多一个标识例如 dynamic-datasource-spring-boot3-starter。如果你的项目已经升级到 Spring Boot 3.2不要拿普通版硬刚否则 javax 和 jakarta 的包名冲突会让你在启动阶段就翻车。2.2 多数据源 yml 编排依赖装好之后核心配置集中在 application.yml。我的习惯是先用 spring.datasource.dynamic 这个根节点把多数据源关系维护清楚再在具体数据源里把 Druid 的连接池参数带上。spring: datasource: type: com.alibaba.druid.pool.DruidDataSource dynamic: primary: master strict: false datasource: master: url: jdbc:mysql://10.0.0.1:3306/order_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://10.0.0.2:3306/order_db_read?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: read_user password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver report: url: jdbc:mysql://10.0.0.3:3306/report_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: report_user password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20关键参数不用全背但要理解它们各自防的是什么坑。initial-size 是启动时建立的连接数如果太小刚启动那会儿流量一大就会频繁建连。test-while-idle 为 true 时连接在空闲回收线程扫描时会被 validation-query 验证避免把已经被 MySQL 服务端掐断的连接继续分给业务。max-wait 设置为 60000 毫秒表示拿连接超过 60 秒就报错而不是无限等这是防止连接池被耗尽后所有线程全部挂死的关键开关。2.3 让每个数据源真正走 Druid 连接池有些人配置完上面的 yml 会发现 Druid 的监控页面 0 数据或者连接池参数根本没有生效。原因通常是 dynamic-datasource 没有识别到你希望用 Druid 作为连接池。在 spring.datasource 根节点上声明 type 只是给了 Spring Boot 一个提示但在 dynamic 场景下更保险的做法是在每个数据源配 driver-class-name 和 type或者至少在全局 datasource 下声明 type: com.alibaba.druid.pool.DruidDataSource。我通常还会把 druid 节点放到 dynamic 下面而不是每个数据源都重复写一遍。这样 master、slave、report 都会继承全局的连接池参数只有个别库需要单独调参时再在对应数据源下覆盖。spring: datasource: dynamic: datasource: master: druid: max-active: 30master 只有 30 个连接起稍大一点其他库仍然沿用全局 20这种优先级的处理方式在多库场景里很实用。2.4 Spring Boot 3 与参数命名的小提醒如果你的项目是 Spring Boot 3.2把 dynamic-datasource 换成 boot3 版本之后Druid 的 druid-spring-boot-starter 也要关注包名兼容问题建议直接使用 1.2.18 以上版本。另外在 Spring Boot 2.4 之后配置文件里的宽松绑定策略有变化druid 节点下的驼峰参数要严格按规范写例如 max-pool-prepared-statement-per-connection-size 这种长参数不要漏掉连字符否则可能静默绑定失败启动日志里不会有明显报错但连接池参数就是不对很难排查。3. Druid 监控与数据库密码加密3.1 开监控页面的两种姿势Druid 的监控页面我一般建议在测试环境开线上看情况但最好统一走鉴权。第一种方式是直接使用 druid-spring-boot-starter 提供的自动配置在 application.yml 里声明 StatViewServlet 的参数spring: datasource: druid: stat-view-servlet: enabled: true url-pattern: /druid/* login-username: monitor login-password: your-password web-stat-filter: enabled: true url-pattern: /* exclusions: *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*第二种方式是手写一个 Configuration 类自己定义 ServletRegistrationBean。我更喜欢这种方式因为可以把初始化参数、IP 白名单、登录账号都集中到一个类里维护而且不容易被自动配置的开关绕过去。Configuration public class DruidMonitorConfig { Bean public ServletRegistrationBeanStatViewServlet statViewServlet() { ServletRegistrationBeanStatViewServlet registration new ServletRegistrationBean(new StatViewServlet(), /druid/*); registration.addInitParameter(loginUsername, monitor); registration.addInitParameter(loginPassword, your-password); registration.addInitParameter(resetEnable, false); registration.addInitParameter(allow, 127.0.0.1,10.0.0.0/24); registration.addInitParameter(deny, ); return registration; } Bean public FilterRegistrationBeanWebStatFilter webStatFilter() { FilterRegistrationBeanWebStatFilter registration new FilterRegistrationBean(new WebStatFilter()); registration.addUrlPatterns(/*); registration.addInitParameter(exclusions, *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*); registration.addName(webStatFilter); return registration; } }allow 参数是关键Web 应用如果部署在内网建议只允许内网网段访问监控页面。resetEnablefalse 是防止有人通过 reset-all 按钮把统计数据清掉生产环境尤其要关掉。deny 参数如果留空字符串表示不拒绝任何来源所以不要写 deny 项直接用 allow 白名单收口更稳妥。3.2 监控页面上能看什么Druid 的监控数据价值很高它统计的是连接池和 SQL 两个维度的信息。连接池维度你能看到当前活跃连接数、空闲连接数、等待获取连接的线程数、逻辑连接关闭次数这些数据能快速判断连接池容量是否够用。SQL 维度更直接每次 SQL 的执行次数、总耗时、最大耗时、平均耗时、返回行数、更新行数都会出现在列表里而且支持按慢 SQL 排序。当某个接口突然变慢打开 Druid 监控页按 ExecuteTime 倒序基本能第一时间锁定是一条大查询导致还是连接池争抢导致。慢 SQL 统计依赖 StatFilter这个过滤器在下一章会重点展开。3.3 用 ConfigTools 给数据库密码加密这也是若依那套框架里被反复验证过的方式。Druid 提供了 ConfigTools 工具类借助 RSA 非对称加密把明文密码变成密文放进配置文件数据库连接时再用公钥解密。过程和步骤大致如下先在本地执行命令用 druid 的 jar 包生成密钥对java -cp druid-1.2.20.jar com.alibaba.druid.filter.config.ConfigTools 你的明文密码执行完会输出 privateKey、publicKey、password 三个值。privateKey 只用于本地生成密文生产环境不要出现publicKey 放到配置文件里作为解密公钥password 字段里填的是生成的密文。配置文件这么写spring: datasource: dynamic: druid: connect-properties: config.decrypt: true config.decrypt.key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ... datasource: master: password: IcD7R3dRM7Fb3ZqZvY9Ntq7Y4qG6sTfWAbMv...密文这里提醒一个坑在多数据源场景下配置键不要写在单数据源的 url 参数里也不要混在普通的 druid 属性里。connect-properties 才是真正传给 DruidDataSource 的 Properties。如果用了 spring.datasource.druid.connection-properties但 dynamic 节点下没有把它透传到具体数据源解密就不会生效启动时会直接报密码解密失败。ConfigTools 这种方式的安全性比明文密码好很多但不代表可以把数据库密码随意写到前端代码或 Git 里。真正要安全还是建议配合配置中心或环境变量把 publicKey 再单独管理起来。若依有的版本会在 Nacos 里下发一部分参数配置中心的方式天然能减少部署时手工改配置的成本。3.4 监控页面隐藏的额外保护如果你项目里已经引入了 Spring Security更稳妥的做法是让 Druid 监控页的 URL 不进 Spring Security 的匿名白名单而是要求登录后才能访问。简单示例就是 SecurityConfig 里直接对 /druid/** 做权限控制访问时需要拥有 admin 角色。Druid 自带的 loginUsername 和 loginPassword 只是表单登录层和框架层面的权限拦截可以叠加使用不要因为是内网就省略。4. SQL 过滤器WallFilter、StatFilter、日志 Filter4.1 Druid 的过滤器链是怎么工作的Druid 的 filter 机制可以理解成连接池层面的一组“门禁”。业务代码拿到的是 Druid 连接每次 statement 执行时要穿过过滤器链经过的过滤器决定了这次 SQL 是被放行、被统计、被改字符集还是被直接拦截。常用过滤器有 StatFilter 负责统计和慢 SQL 记录WallFilter 负责 SQL 防火墙和防注入Slf4jLogFilter 和 Log4j2Filter 负责打印 SQL 日志还有 EncodingConvertFilter 可以处理 GBK 和 UTF-8 转换。每个 filter 都是按顺序执行所以你可以同时开 StatFilter 和 WallFilter先经过防火墙检查再被统计模块计数。有一个容易混淆的点Druid 的 SQL 过滤器和 MyBatis 的 Interceptor 不是一个层面的东西。MyBatis-Plus 的分页插件、乐观锁插件是作用在 MyBatis 执行器上的它们能拿到参数、改写 SQL、拦截执行过程而 Druid filter 作用在 JDBC 连接层面对的是最终要发送给数据库的 SQL 文本。两者可以共存各管一段。4.2 WallFilterSQL 防火墙调优WallFilter 的价值不只是防 SQL 注入它更像个“业务 SQL 守门员”。很多团队会不小心把允许事务里用分号拼接多条 SQL 的习惯带到生产环境一旦遇到 WallFilter默认会直接报错。我给你整理了几个最常调整的开关配置项默认值作用典型调整场景multiStatementAllowfalse是否允许一个 Statement 执行多条 SQL批量初始化脚本需要执行多条语句时打开noneBaseStatementAllowfalse是否允许非基础语句如 SHOW、USE、SET某些运维工具连库时会被拦可临时放行deleteAllowtrue是否允许 DELETE 语句业务如果只有逻辑删除想彻底禁物理删除可设为 falseupdateAllowtrue是否允许 UPDATE 语句同理可做只读库保护conditionAndTrueChecktrue是否检测 WHERE 11 这类恒真条件防误更新全表强烈建议保持开启selectWhereAlwayTrueChecktrue是否检测 SELECT 恒真条件防拖全表的恶意查询insertAllowtrue是否允许 INSERT只读报表库可设为 falsecommitAllowtrue是否允许显式提交防止业务代码里乱写 commit在 dynamic-datasource 的 yml 里WallFilter 的配置可以这样放spring: datasource: dynamic: druid: filter: wall: enabled: true config: multi-statement-allow: false none-base-statement-allow: false delete-allow: true update-allow: true condition-and-true-check: true强调一下 multiStatementAllow。很多初级开发会用 MyBatis 的 foreach 拼接批量插入但 MyBatis 最终发送的仍然是一条 SQL所以和这个开关没有关系。真正会被 multiStatementAllow 拦截的是直接通过 JDBC 执行包含分号的“多重语句”。如果你在配置中心或初始化 SQL 工具里需要一次执行多条语句再单独开不要全局放开。4.3 StatFilter慢 SQL 统计StatFilter 是监控数据的数据源。没有它Druid 监控页里的 SQL 列表就是空的。常用的全局配置spring: datasource: dynamic: druid: filter: stat: enabled: true log-slow-sql: true slow-sql-millis: 3000 merge-sql: trueslow-sql-millis 设置为 3000表示执行时间超过 3 秒就会记成慢 SQL会在日志里输出。merge-sql 开启后会合并结构相似的 SQL例如同一个 Mapper 方法因为参数不同产生的多条记录会聚合成一条统计避免监控列表被刷屏。StatFilter 和 WallFilter 的开启方式都是一样的但注意别重复注册。如果你自己在配置类里 new 了一个 StatFilteryml 里又开了 filter.stat会看到重复统计甚至异常。同一个数据源上的同类过滤器只保留一种注册方式。4.4 日志过滤别在生产环境打印全量参数日志类过滤器我建议谨慎开启。Slf4jLogFilter 能把执行的 SQL 和参数值全部打印出来本地调试确实好用但生产环境一旦打开日志量会大得吓人而且可能把用户手机号、身份证这类敏感数据刷到日志平台里。真要排查线上问题我通常配合 log-slow-sql 单独看慢 SQL 日志而不是用 SQL 全量日志。如果非要在某个环境临时开启只对 Stderr 或单独一个 logger 输出避免大量日志把磁盘写满。4.5 别忘了 MyBatis-Plus 侧还有自己的拦截器既然标题里提到 SQL 过滤器我想把边界说清楚MyBatis-Plus 的“拦截器”和 Druid 的“过滤器”是两个体系。MyBatis-Plus 自带 PaginationInnerInterceptor 做分页OptimisticLockerInnerInterceptor 做乐观锁还可以自定义 InnerInterceptor 做权限数据过滤。比如“数据权限”这种需求典型的做法是写一个自定义 InnerInterceptor在 SQL 执行前根据当前用户注入部门 ID 或租户 ID 的查询条件。这种过滤发生在 SQL 还未进入 JDBC 层之前做的是 SQL 改写Druid 的 WallFilter 发生在 SQL 已经成型之后做的是安全检查。两者配合业务层的权限过滤加上连接层的安全拦截才能把 SQL 管理做完整。5. DS 切换细节与 XML 同目录配置5.1 DS 注解的正确放法数据源切换的入口是 DS。最典型的用法是加在 Service 实现类或方法上Service public class OrderServiceImpl implements OrderService { Override DS(slave) public ListOrderVO listOrders() { return orderMapper.selectList(...); } Override DS(master) public void createOrder(Order order) { orderMapper.insert(order); } }DS 支持放在类上也支持放在方法上方法上的优先级高于类。如果类上标了 DS(master)方法上标了 DS(report)实际走的是 report 库。另外它也可以放在 Mapper 接口或 Mapper 方法上但我建议还是统一放在 Service 层因为数据源路由属于业务边界放在 Mapper 会让调用方不知道当前到底走了哪个库排查链路会变麻烦。5.2 切换失效的根源自调用和事务顺序DS 的底层切面依赖 Spring AOP所以它和 Transactional 一样对同类内部调用无能为力。如果你在 OrderServiceImpl 的一个方法里直接通过 this 调用另一个带 DS 的方法注解根本不会触发数据源切换可靠度为零。要避免这个问题可以把数据源不同的操作拆到不同的 Service Bean 里互相调用或者用注入的 Mapper 方式绕开自调用。事务边界的问题更隐蔽。Spring 的事务管理器在开启事务时就会把数据源绑定到当前事务上下文。如果在 Transactional 方法内部再通过 DS 切换数据源dynamic-datasource 的实现虽然会尽力帮你处理但从语义上说事务一旦开始连接已经确定这时候再切换数据源很容易出现“你还是跑在旧库”或者“连接被拿到了事务上下文之外”的现象。我的经验法则是先切库再开事务。把 DS 和 Transactional 尽量放在不同方法上让切库发生在事务边界之前。如果有跨库的强一致需求涉及分布式事务那就不是动态数据源能单方面解决的了需要用 Seata 或本地消息表这类方案。5.3 XML 与 Mapper 同目录的配置方法这个点同时也是热搜里出现率很高的问题。很多人习惯把 Mapper 接口放在 com.xxx.mapper 包下然后 XML 也放在同一个包目录里看着整齐但项目一打包XML 文件根本不存在 target 目录里运行时直接报 Invalid bound statement。原因是 Maven 默认只把 src/main/resources 下的文件当作资源src/main/java 下的文件只编译 .java不会复制 XML。解决办法有两个二选一或者两个都做。第一个办法是把 XML 放进 resources 目录下的同一路径比如接口在 com/xxx/mapper/UserMapper.javaXML 就放在 src/main/resources/mapper/UserMapper.xml然后配置 mapper-locationsmybatis-plus: mapper-locations: classpath*:mapper/**/*.xml第二个办法是保留 XML 和 Mapper 接口在同一个包下但要主动告诉 Maven 把这个目录下的 XML 也打包进去build resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource /resources /build两种方式我都用过。如果追求代码阅读时一眼能看到接口对应的 XML第二种更直观如果你有大量分模块或者多 Maven 模块结构建议用 classpath* 配合第一种因为它在多模块场景下能扫描到所有 jar 里的 mapper XML不会漏掉依赖模块里的映射文件。5.4 手动切换数据源的方式注解用多了难免遇到动态场景例如一个方法里要根据某个配置字段决定走哪个库DS 注解没法在运行时变。这时候可以用 dynamic-datasource 提供的上下文 APIDynamicDataSourceContextHolder.push(report); try { reportMapper.querySummary(); } finally { DynamicDataSourceContextHolder.poll(); }push 和 poll 必须成对出现忘记 poll 会把路由状态留在当前线程下一个请求持续串库。这种写法适合极少数迫不得已的场景数量一多就要考虑抽象成策略类不然代码里到处是 push/poll维护起来很痛苦。6. 常见问题速查与避坑整理6.1 高频问题排查表我把自己在实际项目里遇到过的典型问题整理成了表格按“现象 - 原因 - 处理”三条列出来遇到类似问题可以直接对号入座。现象可能原因处理方式DS 不生效SQL 始终走主库同类自调用导致 AOP 没触发拆 Service、注入代理对象或通过 Mapper 强制切库事务方法里切库失败事务开启时连接已绑定原库调整方法边界让 DS 在 Transactional 之前执行多线程下数据源串了线程池复用时路由上下文未清理尽量用注解方式避免手动 push 忘记 pollDruid 监控页打不开或 0 数据StatFilter 未开启或 StatViewServlet 未注册yml 开 filter.stat再注册 StatViewServlet启动报密文解密失败connect-properties 未传给 DruidDataSource检查 config.decrypt 和 config.decrypt.key 是否在正确节点WallFilter 一直拦截自己的 SQLSQL 里包含多条语句或非基础语句单独调整对应开关或加白名单规则XML 找不到 bound statementXML 没被 Maven 打包到 classpath配置 mapper-locations 或增加 resource 打包规则只有部分库 Druid 参数生效全局 druid 配置和数据源级配置冲突简化成只维护全局配置单独库再覆盖6.2 Spring Data JPA 和 MyBatis-Plus 怎么选既然热搜里有这个问题我多说两句。JPA 和 MyBatis-Plus 的本质差别在数据访问层的抽象方式。JPA 以“实体状态管理”为核心对象关系映射做得彻底实体一改Hibernate 会自动同步数据库适合业务规则强、领域模型重的项目。MyBatis-Plus 以“SQL 为中心”把 SQL 的编写能力保留在开发者手里适合对查询性能、复杂 SQL、存储过程依赖较多的项目。在多数据源场景下MyBatis-Plus 明显更有优势。原因有两个一是 MyBatis 本身和数据库方言之间没有 JPA 那么厚的一层抽象跨库切换时行为更可控二是 dynamic-datasource 和 MyBatis-Plus 的配合生态已经非常成熟几乎零成本接入。而 JPA 在多数据源场景里EntityManager 和事务管理器的绑定关系更复杂一旦出现跨库操作排查成本比 MyBatis 高不少。如果团队更擅长 SQL或者现有报表类型需求很多直接选 MyBatis-Plus 是更稳妥的方向。6.3 上线前容易返工的三个点第一数据库密码加密不能在最后一刻才想起来。Druid 解密需要公钥和密文配对配置节点一旦写错线上启动就是灾难。建议在联调阶段就把密文格式和 connect-properties 验证过而不是等到部署前才处理。第二WallFilter 不要一上来就全局关闭。很多团队上线遇到莫名奇妙被拦截的 SQL第一反应是直接在 yml 里把 wall.enabled 改成 false这个操作等于把 SQL 防火墙裸奔。正确做法是定位到具体拦截原因判断是不是业务必须的多语句或非基础语句再针对具体开关做调整。第三监控页面开在生产环境一定要有鉴权。Druid 页面除了能看 SQL 还能看 Session 和内存信息等于给攻击者提供了内部结构的地图。至少配登录账号和 allow 白名单有条件再挂一层 Spring Security。最后再说几句这套配置我前后用了大半年整体感受是 dynamic-datasource 加上 Druid 的组合已经很成熟真正会翻车的地方几乎都集中在上文提到的 AOP 边界和过滤选择上。我现在新项目默认会把 WallFilter 打开再配一份慢 SQL 统计监控页面只限内网访问密码走 ConfigTools 加密。最后分享一个小技巧如果你同时管理多个环境配置建议把 druid 节点下的 connect-properties 单独拆成一个环境变量引用这样同一套 yml 可以无缝在测试和正式环境间切换不用每次上线前手工挑配置。踩过几次坑之后你会发现多数据源配置的难点从来不在于“能不能连上多个库”而在于连接池、事务和过滤规则这三者的边界是否清晰。
返回列表