当上层框架(Hibernate、Mybatis)在调用DataSource的getConnection()方法获取数据库连接时,如果DataSource的具体实现是DruidDataSource,那么工作原理如下。
首先DruidDataSource的核心是一个DruidConnectionHolder[]数组,它就是所谓的连接池,DruidConnectionHolder中定义了一个Connection类型的connection属性,所以DruidConnectionHolder就表示一个连接,比如如果用的Mysql,那么connection属性对应的就是ConnectionImpl对象。
当调用DruidDataSource的getConnection()方法时,核心就是从DruidConnectionHolder[]数组获取出缓存好的DruidConnectionHolder对象,获取方式的源码为:
if (maxWait > 0) { holder = pollLast(nanos); } else { holder = takeLast(); }
也就是每次都是获取DruidConnectionHolder[]数组中最后一个DruidConnectionHolder对象,拿到DruidConnectionHolder对象后会检查数据库连接是否可用,比如,数据库重启了,应用没有重启,那么DruidConnectionHolder[]数组中对应的数据库连接其实都已经是不可用的了。
在从DruidConnectionHolder[]数组中拿到一个DruidConnectionHolder对象后,会验证该对象的数据库连接是否可用,判断方式为基于当前DruidConnectionHolder对象中的数据库连接执行一个SQL,比如:
Mysql默认为:"SELECT 1" Pg默写人为:"SELECT 'x'"
当然,你可以修改Druid的配置validationQuery来修改所执行的SQL。
但是拿到DruidConnectionHolder对象后,并不是一定会进行验证,需要看Druid的其他配置,比如:
1、testOnBorrow如果为true,那么每拿出一个DruidConnectionHolder对象,都会进行验证,所以testOnBorrow等于true比较消耗性能,因为每次从连接池获取连接都会进行验证,如果验证通过则直接返回给上层框架,如果验证不通过,则会从DruidConnectionHolder[]数组中取下一个DruidConnectionHolder对象,然后继续进行验证,如果DruidConnectionHolder[]数组中没有下一个了,则等待maxWait指定的时间,如果这段时间内有归还的DruidConnectionHolder对象,那么就直接取出来,如果超过maxWait时间还没有拿到,则会抛出GetConnectionTimeoutException。
2、如果testOnBorrow为false,默认就是false,那么则会判断testWhileIdle是否为true,默认为true,则会用当前时间减去DruidConnectionHolder对象的lastActiveTimeMillis,从而得到DruidConnectionHolder对象的空闲时间,也就是这个DruidConnectionHolder对象有多久时间没有执行过SQL了,如果这段空闲时间超过了timeBetweenEvictionRunsMillis,默认为60s,才会进行验证,也就是说,testWhileIdle为true表示Druid只会对那些空闲时间超过60s的连接进行验证,如果正好这60s内数据库重启了,或者对应的真实数据库连接被人工kill掉了,那么Druid是不会进行校验的,这样上层Hibernate或Mybatis拿到的就是一个不可用的数据库连接,最终基于该连接是执行不了SQL的。
3、当然,上层框架在使用完一个数据库连接后,会调用close()方法来关闭数据库连接,而底层其实就会调到DruidConnectionHolder对象的close()方法,从而会判断testOnReturn是否为true,默认为false,如果为true,则同样会进行验证,如果验证通过,则会将该DruidConnectionHolder对象重新putLast()到DruidConnectionHolder[]数组中,如果验证不通过那么就不会putLast()了。
另外,Druid拿到一个DruidConnectionHolder对象后,会判断配置defaultAutoCommit是否为false,如果是false,则会设置底层数据库连接的autocommit也为false。
还没有评论,来说两句吧...