我们先回顾上一篇 《DAS解决访问数据库延时突高的案例分享》 。上一次为了解决数据库偶发连接高耗时的问题,我们将参数 minIdle 从0改为1,在连接池中始终保留一个连接。然后为了确保连接的有效性,又把参数 testOnBorrow 改为true。成功解决偶发高延时问题。随后,我们DAS团队对Tomcat数据源做了更多的了解和研究,包括研读它的源代码,发现它仍旧有优化的空间。
我们上次调优主要是将 testOnBorrow 配置为true。虽然保证了返回连接的有效性,但是这也意味着多了一次数据库连接执行'SELECT 1'的检查。我们进一步分析性能消耗时,发现Tomcat数据源并非每次都会做检查,如果在最近一个时间段内( validationInterval 参数)这个连接已经被检查过,那么就不会再做检查。虽然这个机制减小了检查操作的几率,但还是存在做检查可能性,不够完美。毕竟作为一款几百个应用都使用的产品,哪怕一次细小的改进都会有巨大的 积累效应 。
怎么样才能在 testOnBorrow =false的配置下,最大程度保证返回连接的有效性呢?要达到这样的效果,就需要对Tomcat数据源有更深入的理解。因此本文会从介绍Tomcat数据源的内部构造和原理入手来介绍这次优化工作。
Tomcat数据源是对JDBC DataSource的一个具体实现。根据官方对DataSource的解释,DataSource除了提供数据库连接Connect的功能(pooled connections)之外,它还能提供分布式事务的支持(distributed transaction)。
在实际工作中,我们主要把它作为connection pool来用,因此它本质是一个 缓存系统 ,它缓存对象的是数据库连接Connect。
对于一个缓存系统来说,它最 核心功能 是能够自动剔除(evict)不再需要的缓存对象,否则它只是个简单的map。所以如果你能搞清楚缓存的evict机制,那么你对这个缓存系统就有了最核心的理解。那我们就来看看Tomcat数据源作为缓存系统,它的evict机制是什么样的?
先来看evict的策略。一般evict机制有两种策略:time based和size based。time based是根据缓存对象的过期时间来判断,譬如规定存在超过1分钟的对象就需要从缓存中被剔除。size based是一旦缓存对象数目超出阈值系统就开始evict,否则会产生内存或者资源的泄漏。Tomcat数据源同时采用了这种两种策略。做evict的时候,既要判断数据库连接对象存在的时间,同时又要保证数据库连接的总数保持在阈值之内。
那evict在什么时机触发呢?触发evict可以有两种做法:
一:在取get或者存put的时候,检查缓存内容,剔除不再需要的缓存对象。有些本地缓存软件就用这种做法,它比较简单,不需要额外线程,但是存取的操作需要花费额外检查的逻辑。
二:后台启动一个检查缓存的线程,定期检查剔除不再需要的缓存对象。Tomcat数据源使用的是第二种做法。
我们再结合Tomcat数据源内部基本的数据结构来看evict机制。
Tomcat数据源内部数据结构大致会分为两个集合:idle集合和busy集合,两个集合里放的都是数据库连接。Idle集合就是一个缓存集合,专门存放未被被应用使用的数据库连接,而busy集合指的是被应用正在使用的连接。一般情况下,当应用从数据源获取数据库连接的时候,一个数据库连接会从idle集合中去获取,然后进入busy集合。当使用结束后再从busy集合回到idle。
数据库连接就在这两个集合之间移动。连接从idle集合移到busy集合叫做borrow,会触发 testOnBorrow 的事件;反之,连接从busy集合移到idle集合叫做return,会触发 testOnReturn 的事件。用户可以利用这两个事件,对连接的有效性做检查,将失效连接剔除出数据源。
一图胜千言:
了解了静态数据结构之外,很重要的是理解它后台线程做的工作。这个线程的名字是 PoolCleaner ,从这个命名也能猜出它的功能。它会定期检查idle集合,剔除超过那些时间超出 minEvictableIdleTimeMillis ,以及通不过 testWhileIdle 检测的连接。
对Tomcat数据源的内部基本结构有了了解之后,我们可以看一下这一次我们具体的优化点。
和之前的流程一样,我们先在本地开发环境做了充分的测试,然后上测试环境观察了一周左右。最后上预发和生产环境观察连续观察几天,没有发生任何问题,优化成功。
我们这两次的优化结果都是成功的,但是从另外的角度来看本质又有不同:上一次是被动的用户问题驱动的优化,这一次是主动技术优化。主动技术优化,我们也是出于两方面的考量:
首先,从技术上来看,虽然这次做的是比较小的优化,但是作为一个服务于几百个应用的组件来说,它的积累效应会被放大。这也是我们中间件团队有别于其他技术团队的一个特点。中间件产品的一个细小的优化,为公司带来的可能就是成千上万个服务器上的CPU,内存和网络开销的节省。常言道 “勿以善小而不为”,对于我们中间件团队来说就是“ 勿以优化小而不为 ”。其次,从人的学习的规律来看,当第一次进入一个领域研究学习之后,称热打铁也更有利于知识的深度学习和积累,学无止境。这些就是我们再次做优化的动力!
DAS项目从去年开始已经在GitHub上开源,欢迎同学们去clone,fork,提问和加星★: https://github.com/ppdaicorp/das
Shengyuan,信也科技布道师、框架中间件资深专家、目前主要从事DAS相关的研发、运维工作。