Loading... 在现代的Web应用开发中,高性能的数据库连接池 和 多数据源的读写 是确保应用稳定性和响应性的关键因素之一。 Druid是一个阿里巴巴结合了c3p0、DBCP等数据库连接池的优点开源的数据库连接池和SQL查询优化工具,用于提高应用程序对数据库的性能和可扩展性。 主要提供的功能:数据库连接池、数据库连接池监控、SQL查询优化、数据源管理、防御SQL注入、统计和监控。 SpringBoot默认使用的`com.zaxxer.hikari.HikariDataSource`作为数据源。 接下来 将 Druid 连接池 和 Dynamic 多数据源的步骤,以及如何通过其丰富的特性优化应用的数据库性能。 ## 引入依赖 ```java <properties> <druid.version>1.2.23</druid.version> <dynamic-ds.version>4.2.0</dynamic-ds.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version> </properties> <dependencies> <!-- MySQL 驱动依赖 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <!-- MyBatis Plus 依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> <exclusions> <!-- 排除默认的 HikariCP 数据源 --> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency> <!-- Alibaba Druid 德鲁伊 数据库 资源池 起步依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <!-- Dynamic DataSource 动态多数据源 起步依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${dynamic-ds.version}</version> </dependency> </dependencies> ``` ## 定义多数据源注解 我们这里自定义了`Master`和`Slave`,可以根据自己的业务需求进行定义 ```java package com.if010.datasource.annotation; import com.baomidou.dynamic.datasource.annotation.DS; import java.lang.annotation.*; /** * 主库数据源 * @author Kim同学 */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @DS("master") public @interface Master { } ``` ```java package com.if010.datasource.annotation; import com.baomidou.dynamic.datasource.annotation.DS; import java.lang.annotation.*; /** * 从库数据源 * @author Kim同学 */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @DS("slave") public @interface Slave { } ``` ## 配置文件 由于`druid-spring-boot-starter`和`dynamic-datasource-spring-boot-starter`都是已经封装好了的起步依赖,专用于集成 SpringBoot 项目,无需编写配置类文件,除非你想个性化定制某些配置。这里就直接按照SpringBoot整合其他技术的通用规则来,导入对应的starter,进行相应的配置即可。 ```yaml spring: datasource: druid: # 初始化连接大小 initial-size: 5 # 最小空闲连接数 min-idle: 5 # 最大连接数 max-active: 20 # 配置获取连接等待超时的时间(单位:毫秒) max-wait: 60000 # 连接超时时间 connectTimeout: 30000 # socket超时时间 socketTimeout: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 2000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 600000 max-evictable-idle-time-millis: 900000 # 用来测试连接是否可用的SQL语句,默认值每种数据库都不相同,这是mysql validation-query: select 1 # 应用向连接池申请连接,并且testOnBorrow为false时,连接池将会判断连接是否处于空闲状态,如果是,则验证这条连接是否可用 test-while-idle: true # 如果为true,默认是false,应用向连接池申请连接时,连接池会判断这条连接是否是可用的 test-on-borrow: false # 如果为true(默认false),当应用使用完连接,连接池回收连接的时候会判断该连接是否还可用 test-on-return: false # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle poolPreparedStatements: true # 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true, # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题, # 可以把这个数值配置大一些,比如说100 maxOpenPreparedStatements: 20 maxPoolPreparedStatementPerConnectionSize: 20 # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作 keepAlive: true # 配置监控统计拦截的filters, stat:监控统计、log4j:日志记录、wall:防御sql注入 filters: stat,wall,log4j2 # 合并多个DruidDataSource的监控数据 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 stat-view-servlet: # 是否开启 Druid Monitor 后台管理配置 enabled: true # 配置访问路径 (默认:/druid/*) DruidStatView: /druid/* # 配置 Druid Monitor 后台 登录用户名 loginUsername: if010 # 配置 Druid Monitor 后台 登录密码 loginPassword: iufx+ekhh47ao7ol # 访问黑白名单配置 (deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝,如果allow没有配置或者为空,则允许所有访问) # allow: # deny: 127.0.0.1 # 是否允许清空统计数据 (默认: true) reset-enable: false # druid 监控配置 filter: # 用于统计监控信息 stat: # 是否启用 enabled: true db-type: mysql # 开启慢sql监控,超过2s 就认为是慢sql,记录到日志中 log-slow-sql: true slow-sql-millis: 10 # 合并相同sql的统计 merge-sql: true wall: # 是否启用sql检测功能 ,防止sql注入攻击,sql防火墙启用 enabled: true db-type: mysql config: delete-allow: false drop-table-allow: false # 日志监控,使用slf4j 进行日志输出 slf4j: enabled: true statement-log-error-enabled: true statement-create-after-log-enabled: false statement-close-after-log-enabled: false result-set-open-after-log-enabled: false result-set-close-after-log-enabled: false # 配置WebStatFilter,用于采集web关联监控的数据 web-stat-filter: # 启动 StatFilter enabled: true # 过滤所有url url-pattern: /* # 排除一些不必要的url exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" # 开启session统计功能 session-stat-enable: true # session的最大个数,默认100 session-stat-max-count: 10 # 配置资源池使用 Alibaba Druid(德鲁伊) 阿里巴巴开源的项目,不设置则为Spring默认的资源池 "com.zaxxer.hikari.HikariDataSource" type: com.alibaba.druid.pool.DruidDataSource # 多数据源配置 dynamic: primary: master strict: false datasource: # 主库数据源 master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://dev.if010.com:3306/if010_system?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: ENC(NLSLTSpjLuPp2uo0BMO4hGzhEWG+/KzCH1bCI8K6S7Zr5rTE88aebn21psS+eiegE6YFJ9KbexZVmEx0Sb+bBA==) password: ENC(CJNwnl5NwJ4tsqGAtyUvOT0MdhvW8hw4myz5rTFX44vmSNaUj605AZWXasEbiTw3mVriSXjTspaFxdRhBMl6eQ==) public-key: ${publicKey} # 从库数据源 slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://dev.if010.com:3306/if010_system_slave?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: ENC(NLSLTSpjLuPp2uo0BMO4hGzhEWG+/KzCH1bCI8K6S7Zr5rTE88aebn21psS+eiegE6YFJ9KbexZVmEx0Sb+bBA==) password: ENC(CJNwnl5NwJ4tsqGAtyUvOT0MdhvW8hw4myz5rTFX44vmSNaUj605AZWXasEbiTw3mVriSXjTspaFxdRhBMl6eQ==) public-key: ${publicKey} mybatis-plus: # Mappler.xml文件地址,默认值 mapper-locations: classpath*:mapper/*.xml # 全局配置 global-config: db-config: # ID自增长 id-type: auto # 更新策略: 只更新非空字段 update-strategy: not_null configuration: # 开启 Mybatis 驼峰命名映射,Mybatis数据库与实体类自动映射 map-underscore-to-camel-case: true # mybatis 日志输出为控制台标准输出 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl use-generated-keys: true default-statement-timeout: 60 default-fetch-size: 100 # 是否开启二级缓存 cache-enabled: true ``` ## 食用测试 项目服务模块启动后我们可以通过`http://${service.address}:${service.port}/druid`访问Druid Monitor的后台页面  这里我们使用上面配置好的用户名密码登录即可进入  可以在数据源看到我们配置文件里面定义的Dynamic多数据源Master 和 Slave两个数据源,在别的栏目我们可以看到SQL监控、防火墙、web应用等等的监控记录、日志统计数据 到目前为止说明我们一切都已经就绪了 但是,为了实际生产环境的安全着想,我们一般会把密码加密配置,而不是赤裸裸的使用铭文进行配置,接下来我们就进行这一加密配置操作 ## 敏感配置加密 Druid 里非常贴心的为我们定义好了一个工具类`com.baomidou.dynamic.datasource.toolkit.CryptoUtils`,我们可以用这个工具类对敏感数据进行加密 ```java void contextLoads() throws Exception{ // 生成随机密钥 String[] genKeyPair = CryptoUtils.genKeyPair(512); System.out.println("公钥: \n" + genKeyPair[0]); System.out.println("私钥: \n" + genKeyPair[1]); // 利用密钥对用户名密码进行加密 String username = CryptoUtils.encrypt(genKeyPair[0], "root"); String password = CryptoUtils.encrypt(genKeyPair[0], "123456"); System.out.println("加密后的用户名 - username: " + username); System.out.println("加密后的密码 - password: " + password); } ``` 执行后的输出 ```xml 公钥: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAuR4IJwfp9OYSM8iBSG9Pdd7DMViL7/KUEWzDuyhce2g6DNnuUJdvMRkSP874+bWoKtC/BFPb3FvmKVdWtmX0gQIDAQABAkEAhWsRsR/RDQKs58mA9DXVbTyKLgyFfwC0mwJq538leILh97og0TcvNI90buCytzsMPEo4hfrbPVBvXbq6XgNxYQIhAOSbrsRryPDUMp1Y5IrS9dTFvlVH3oOd3YK/rgTdat9VAiEAz0xQMyS/7D7szaT41p/RkrqjPLfPwGmHZHPUF6rLSH0CIQCQLrh9XdWwPsVlhAkwnXk6pzUyKE/pBwoMSsSAgP3EuQIgMooee3mYORbWUQTJjjoEtEtAhQsjYXvZAIqkyOhhC1kCIBGTRmPrO8AalwRYzslPsjdJEuYmNmpxKpJtcVS4V6Ao 私钥: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALkeCCcH6fTmEjPIgUhvT3XewzFYi+/ylBFsw7soXHtoOgzZ7lCXbzEZEj/O+Pm1qCrQvwRT29xb5ilXVrZl9IECAwEAAQ== 加密后的用户名 - username: NIgB+M+Yb23w9hsiVmn5tE2OvoHZY5tJJq7zOp3u19xObl162JD/ml259O3aO994dN94Of2hBKTy9dSsvvG8Qg== 加密后的密码 - password: sKbObhfhUezf712JEdy53/sWS1cD0RxLZfam+VmHCtbe5ghFc1X8zvqRugQKqZhd2/08E6ldwv/YPdVc13l+gw== ``` 接下来我们将输出的密文配置到配置文件当中 ```yaml datasource: # 主库数据源 master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/master_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true # ENC括号内为加密内容 username: ENC(NIgB+M+Yb23w9hsiVmn5tE2OvoHZY5tJJq7zOp3u19xObl162JD/ml259O3aO994dN94Of2hBKTy9dSsvvG8Qg==) password: ENC(sKbObhfhUezf712JEdy53/sWS1cD0RxLZfam+VmHCtbe5ghFc1X8zvqRugQKqZhd2/08E6ldwv/YPdVc13l+gw==) public-key: ${publicKey} # 从库数据源 slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/slave_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true # ENC括号内为加密内容 username: ENC(NIgB+M+Yb23w9hsiVmn5tE2OvoHZY5tJJq7zOp3u19xObl162JD/ml259O3aO994dN94Of2hBKTy9dSsvvG8Qg==) password: ENC(sKbObhfhUezf712JEdy53/sWS1cD0RxLZfam+VmHCtbe5ghFc1X8zvqRugQKqZhd2/08E6ldwv/YPdVc13l+gw==) public-key: ${publicKey} ``` > 这里要注意的是因为我们使用的是非对称加密,需要配置public-key私钥解密,public-key与password为同一级,切记层级不能配错 > > 由于如果我们将public-key私钥配置在文件当中的话,别人可以拿着私钥解密,这样就会失去加密配置的意义了,所以我们这里使用变量,项目启动配置`java -jar xxx.jar --publicKey=xxxxxxxxxx`,如果使用idea的话,可以在启动配置中的Program arguments配置 最后修改:2025 年 07 月 02 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 -