DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池。
(1)DataSource中URL重要配置参数(Spring配置前缀:spring.datasource.url)
配置缺省值说明推荐说明serverTimezone 时区Asia/ShanghaiuseUnicode truecharacterEncoding1、存数据
数据库在存放项目数据的时候会先用UTF-8格式将数据解码成字节码,然后再将解码后的字节码重新使用GBK编码,并存放到数据库中。
2、取数据
在数据库中取数据的时候,数据库会先将数据库中的数据按GBK格式解码成字节码,然后再将解码后的字节码重新按UTF-8格式编码数据,最后再将数据返回给客户端
UTF8useSSLtrueMySQL在高版本需要指明是否进行SSL连接false(2)Druid重要配置参数(Spring配置前缀:spring.datasource.druid)
配置缺省值描述推荐说明initial-size0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时连接数=(核心数 * 2) + 有效磁盘数)min-idle 最小连接池数量与initialSize一致max-idle8最大连接池数量 max-wait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁 use-unfair-lockfalse如果设置了maxWait,则启用公平锁 time-between-eviction-runs-millis60000在空闲连接回收器线程运行期间休眠时间,它决定线程多久验证空闲连接或丢弃连接的频率源码上看,是通过validation-query配置的语句语句检测连接的有效性,如果连接失败了则丢弃min-evictableIdle-time-millis1800000连接在池中保持空闲而不被回收的最小时间 validation-query 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用false如果检测到已知驱动后,会使用MySqlValidConnectionChecker类进行有效检测,走的是ping策略,而不是该配置,哪怕没有配置也会走默认的 “select 1”,前提是使用MySqlValidConnectionChecker,不然是直接返回true的validation-query-timeout-1检测连接超时时间虽然这里默认是-1,但是实际代码会设置成1s的时间test-on-borrowfalse申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 test-while-idletrue建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 test-on-returnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 pool-prepared-statementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭 max-open-prepared-statements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 filter-class-names flter.stat.slow-sql-millis
以数据库实例是4核8G为例(待考量):
spring: datasource: druid: initial-size: 10 min-idle: 10 max-active: 25 max-wait: 60000 use-unfair-lock: true time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000设置后,性能会翻倍(见参考资料有赞优化),但是这样做有没有其他问题?
(1) com.alibaba.druid.pool.DruidDataSource#getConnectionDirect
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { int notFullTimeoutRetryCnt = 0; for (;;) { // ... if (testOnBorrow) { // ... } else { if (poolableConnection.conn.isClosed()) { discardConnection(poolableConnection.holder); // 传入null,避免重复关闭 continue; } if (testWhileIdle) { final DruidConnectionHolder holder = poolableConnection.holder; long currentTimeMillis = System.currentTimeMillis(); long lastActiveTimeMillis = holder.lastActiveTimeMillis; long lastExecTimeMillis = holder.lastExecTimeMillis; long lastKeepTimeMillis = holder.lastKeepTimeMillis; if (checkExecuteTime && lastExecTimeMillis != lastActiveTimeMillis) { lastActiveTimeMillis = lastExecTimeMillis; } if (lastKeepTimeMillis > lastActiveTimeMillis) { lastActiveTimeMillis = lastKeepTimeMillis; } long idleMillis = currentTimeMillis - lastActiveTimeMillis; long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis; if (timeBetweenEvictionRunsMillis <= 0) { timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; } // 如果空闲时间达到阙值 if (idleMillis >= timeBetweenEvictionRunsMillis || idleMillis < 0 // unexcepted branch ) { // 检测连接是否有效 boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn); if (!validate) { if (LOG.isDebugEnabled()) { LOG.debug("skip not validate connection."); } // 无效则关闭连接 discardConnection(poolableConnection.holder); continue; } } } } // ... }(2) 关闭连接后,如果当前存活数小于最小连接数
空信号的处理利用的是AQS的Condition.sign(),这里还不确定会不会开启。Condition忘记了~
public void discardConnection(DruidConnectionHolder holder) { if (holder == null) { return; } Connection conn = holder.getConnection(); if (conn != null) { JdbcUtils.close(conn); } lock.lock(); try { if (holder.discard) { return; } if (holder.active) { activeCount--; holder.active = false; } discardCount++; holder.discard = true; if (activeCount <= minIdle) { emptySignal(); } } finally { lock.unlock(); } }
【有赞DB连接池性能优化】
【数据库连接池的大小配置】