2
回答
druid连接池的总量低于配置的minIdle
终于搞明白,存储TCO原来是这样算的>>>   

问题:

我们线上配置的具体配置 minIdle=20,详情见附件1

druid日志打印出来 activeCount+poolingCount=4 具体日志见附件2.

连接池的总量已经低于最小值了。

出现这种原因:

   业务从连接池获取连接后,testWhileIdle,发现连接失败(注:超过服务端空闲时间,已经被数据库服务端强行关闭,目前我们的运维体系里,服务端关闭被强行定义为15分钟)此时抛弃该连接,数量从初始值的20变为19.后面反复会陆续降低到业务真正使用的连接数,我们的业务上是4个。

进一步分析:

    对于震荡式业务, 如果业务对连接数需求新增,会新建连接,连接池数量从4个增加到6个(在我们的业务场景下),然后业务需求降低,会有几个连接一直不被使用(连接池数据结构使用的数组堆栈),最终等到业务震荡高峰时被获取,如果刚好超过了minEvictableIdleTimeMillis并且已经被服务端关闭,testWhileIdle后被丢弃。

     这个会导致连接池的意义被降低了,极端情况,对震荡式的业务会导致获取连接时会遇到 1.先testWhileIdle 2.再await等待CreateConnectionThread线程新建连接

代码分析和个人想法:

目前的CreateConnectionThread线程用于新建扩充,DestroyConnectionThread用于收缩:

看了源代码,我们是用的1.0.8 http://grepcode.com/file/repo1.maven.org/maven2/com.alibaba/druid/1.0.8/com/alibaba/druid/pool/DruidDataSource.java#DruidDataSource.shrink%28boolean%29

1. CreateConnectionThread 里面有这段代码

// 必须存在线程等待,才创建连接
                    if (poolingCount >= notEmptyWaitThreadCount) {
                        empty.await();
                    }



  个人看法: 限制会不会太强了,虽然看上去较合理,但无法从4补全到20(minIdle配置值)的数量,失去了人工配置的minIdle意义,也无法很好的处理上面描述震荡式的的业务场景。

2. DestroyConnectionThread 里面shrink代码

final int checkCount = poolingCount - minIdle;
            final long currentTimeMillis = System.currentTimeMillis();
            for (int i = 0; i < checkCount; ++i) {
               //闲置时间判断  和连接缩减         
            }



   这里只对大于minIdle的连接进行缩减。

   个人看法:是不是缺乏了对所有连接的test检查。场景:连接池配置超时时间是4个小时,mysql配置的是2个小时。目前代码会导致一些连接被服务端中断了,但是没有被有效关闭回收。

附件1:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.initialPoolSize=20
jdbc.minPoolSize=20
jdbc.maxPoolSize=50
jdbc.maxWaitTime=6000
jdbc.maxIdleTime=14400000
jdbc.acquireIncrement=5
jdbc.acquireCheckoutTimeout=3000
jdbc.idleConnectionTestPeriod=1800
mysql.driver.slave=com.mysql.jdbc.Driver

<beanid="abstractDataSource"abstract="true"
      class="com.alibaba.druid.pool.DruidDataSource"init-method="init"
      destroy-method="close">
      <propertyname="DriverClassName"value="${jdbc.driver}"/>
      <!-- 配置初始化大小、最小、最大 -->
      <propertyname="initialSize"value="${jdbc.initialPoolSize}"/>
      <propertyname="minIdle"value="${jdbc.minPoolSize}"/>
      <propertyname="maxActive"value="${jdbc.maxPoolSize}"/>
      <!-- 配置获取连接等待超时的时间 -->
      <propertyname="maxWait"value="60000"/>
      <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
      <propertyname="timeBetweenEvictionRunsMillis"value="${jdbc.idleConnectionTestPeriod}"/>
      <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
      <propertyname="minEvictableIdleTimeMillis"value="${jdbc.maxIdleTime}"/>
      <propertyname="validationQuery"value="SELECT 'x'"/>
      <propertyname="testWhileIdle"value="true"/>
      <propertyname="testOnBorrow"value="false"/>
      <propertyname="testOnReturn"value="false"/>
      <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
      <propertyname="poolPreparedStatements"value="false"/>
      <propertyname="maxPoolPreparedStatementPerConnectionSize"
         value="20"/>
      <!-- 配置监控统计拦截的filters -->
      <propertyname="filters"value="stat,log4j"/>
      <propertyname="timeBetweenLogStatsMillis"value="60000"/>
      <propertyname="connectionProperties"
         value="druid.stat.logSlowSql=true;druid.stat.slowSqlMillis=500;druid.stat.mergeSql=true"/>
   </bean>

附件2:

druid监控日志如下,{

  "url": "jdbc:mysql:///dababase?characterEncoding=UTF-8",
  "dbType": "mysql",
  "name": "DataSource-4129454",
  "activeCount": 0,
  "activePeak": 4,
  "activePeakTime": "2016-02-15 22:00:10",
  "poolingCount": 4,
  "poolingPeak": 4,
  "poolingPeakTime": "2016-02-15 21:59:13",
  "connectCount": 9354,
  "closeCount": 9354,
  "executeCount": 9962,
  "errorCount": 1,
  "commitCount": 305,
  "pstmtCacheHitCount": 9956,
  "pstmtCacheMissCount": 6,
  "startTransactionCount": 305,
  "transactionHistogram": [
    0,
    305
  ],
  "connectionHoldTimeHistogram": [
    8999,
    353,
    2
  ]

}

这个是druid打印的log日志


举报
fei33423
发帖于2年前 2回/2K+阅
顶部