druiddatasource配置_数据库连接池配置(案例及排查指南)引⾔
连接池配置
maxWait
参数表⽰从连接池获取连接的超时等待时间,单位毫秒,需要注意这个参数只管理获取连接的超时。获取连接等待的直接原因是池⼦⾥没有可⽤连接,具体包括:连接池未初始化,连接长久未使⽤已被释放,连接使⽤中需要新建连接,或连接池已耗尽需等待连接⽤完后归还。这⾥有⼀个很关键的点是 maxWait 未配置或者配置为 0 时,表⽰不设等待超时时间(可能与⼀些⼈认为 -1 表⽰⽆限等待的预期不符合,虽然在 druid 中 maxWait 配置成 -1 的含义也相同)。对实现细节感兴趣的读者可以从
com.alibaba.druid.pool.DruidDataSource#getConnection ⽅法⼊⼿,查看参数的使⽤逻辑。
推荐配置: 内⽹(⽹络状况好) 800;⽹络状况不是特别好的情况下推荐⼤于等于 1200,因为tcp 建连重试⼀般是 1 秒。
如果不配置maxWait,后果会怎么样呢?可能有些应⽤就这么⼲的,⽽且也没发⽣过异常,不过最终墨菲定律还是会显灵的,下⾯来看⼏个真实的案例:
案例⼀
// 参数配置maxWait=0, maxActive=5, …
正常流量下业务没有发现任何问题,但突发⼤流量涌⼊时,造成连接池耗尽,所有新增的DB请求处于等待获取连接的状态中。由于maxWait=0 表⽰⽆限等待,在请求速度⼤于处理速度的情况下等待队列会越排越长,最终业务上的表现就是业务接⼝⼤量超时,流量越⼤造成实际吞吐量反⽽越低。
案例⼆
maxWait=0, removeAbandoned=true, removeAbandonedTimeout=180, …
现象:业务代码正常运⾏了很长时间没有出现过消息积压情况,在⼀次全链路压测后产⽣⼤量的压测数据,造成了MQ消息的堆积。即使重启服务,也只能保持⼏⼗秒的正常运⾏,随后⼜进⼊消费停滞的状态。使⽤ jstack 发现是卡在获取数据库连接中,再过3分钟左右后出现错误:abandon connection, owner thread: xxx 。最后业务通过配置 maxWait=3000(3秒超时),业务MQ消息正常消费。
原因分析:业务依赖两个数据源,这⾥表⽰为 datasource1 与 datasource2,其中在部分代码段中同时开启了两个库的事务。如图1 所⽰,线程1 获取了 datasource1 中的最后⼀个连接 connection[n],等待获取 datasource2 的连接,此时线程2也正在执⾏类似的操作,造成了死锁等待。⼤家对这种互锁⼀定很熟悉,只是这次是发⽣在DB上。为什么⼀段时间后程序报 abandon connection 的错误,这是
因为配置了 {removeAbandoned:true, removeAbandonedTimeout:180} 这两个参数,这个配置的含义是如果⼀个连接持有 180 秒还没有归还,就被认为是异常连接(对于OLTP的业务查询通常都是毫秒级的),需要关闭掉这条连接。之所以正常情况下没有发⽣问题是因为连接池⽔位⽐较低,资源充⾜没有造成相互等待的情况。
图1. 双DB连接池死锁问题
connectionProperties
参数是以键值对表⽰的字符串,其中可以配置 connectTimeout 和 socketTimeout,它们的单位都是毫秒,这两个参数在应对⽹络异常⽅⾯⾮常重要。connectTimeout 配置建⽴ TCP 连接的超时时间,socketTimeout 配置发送请求后等待响应的超时时间。这两个参数也可以通过在 jdbc url 中添加 connectTimeout=xxx&socketTimeout=xxx 的⽅式配置,试过在 connectinoProperties 中和 jdbc url两个地⽅都配置,发现优先使⽤ connectionProperties 中的配置。如果不设置这两项超时时间,服务会有⾮常⾼的风险。现实案例是在⽹络异常后发现应⽤⽆法连接到DB,但是重启后却能正常的访问DB。因为在⽹络异常下socket 没有办法检测到⽹络错误,这时连接其实已经变为“死连接”,如果没有设置 socket ⽹络超时,连接就会⼀直等待DB返回结果,造成新的请求都⽆法获取到连接。
推荐配置:socketTimeout=3000;connectTimeout=1200
keepAlive
参数表⽰是否对空闲连接保活,布尔类型。可能不少⼈认为 druid 连接池默认会维持DB连接的⼼跳,对池⼦中的连接进⾏保活,特别配置了 minIdle 这个参数后觉得,有了 minIdle 最少应该会保持这么多
空闲连接。其实,keepAlive 这个参数是在 druid 1.0.28 后新增的,并且默认值是 false,即不进⾏连接保活。
那么需要保活连接,是不是将 keepAlive 配置成 true 就完事了呢?虽然 true 的确是开启了保活机制,但是应该保活多少个,⼼跳检查的规则是什么,这些都需要正确配置,否则还是可能事与愿违。这⾥需要了解⼏个相关的参数:minIdle 最⼩连接池数量,连接保活的数量,空闲连接超时踢除过程会保留的连接数(前提是当前连接数⼤于等于 minIdle),其实 keepAlive 也仅维护已存在的连接,⽽不会去新建连接,即使连接数⼩于 minIdle;minEvictableIdleTimeMillis 单位毫秒,连接保持空闲⽽不被驱逐的最⼩时间,保活⼼跳只对存活时间超过这个值的连接进⾏;maxEvictableIdleTimeMillis 单位毫秒,连接保持空闲的最长时间,如果连接执⾏过任何操作后计时器就会被重置(包括⼼跳保活动作);timeBetweenEvictionRunsMillis 单位毫秒,Destroy 线程检测连接的间隔时间,会在检测过程中触发⼼跳。保活检查的详细流程可参见源码com.alibaba.druid.pool.DruidDataSource.DestroyTask,其中⼼跳检查会根据配置使⽤ping或validationQuery 配置的检查语句。
推荐配置:如果⽹络状况不佳,程序启动慢或者经常出现突发流量,则推荐配置为true;
案例⼀
keepAlive=true, minIdle=5, timeBetweenEvictionRunsMillis=10000, minEvictableIdleTimeMillis=1000
00, maxEvictableIdleTimeMillis=100000, …
请问上述配置连接能保活成功吗? 不能,由于 minEvictableIdleTimeMillis == maxEvictableIdleTimeMillis,所以连接在开始检测时就会被断定超过 maxEvictableIdleTimeMillis 需要丢弃。
案例⼆
keepAlive=true, minIdle=5, timeBetweenEvictionRunsMillis=10000, minEvictableIdleTimeMillis=95000, maxEvictableIdleTimeMillis=100000, …
请问上述配置连接能保活成功吗? 具有随机性,由于maxEvictableIdleTimeMillis - minEvictableIdleTimeMillis < timeBetweenEvictionRunsMillis,所以有可能在这个窗⼝期并没有执⾏Destroy线程检测任务,⽆法保证⼼跳⼀定会被执⾏。
maxActive
最⼤连接池数量,允许的最⼤同时使⽤中的连接数。这⾥特地唠叨⼀下,配置 maxActive 千万不要好⼤喜多,虽然配置⼤了看起来业务流量飙升后还能处理更多的请求,但切换到DB视⾓会发现其实连接数的增多在很多场景下反⽽会减低吞吐量,⼀个⾮常典型的例⼦就秒杀,在更新热点数据时DB 需要加
锁操作,这个时候再让更多的连接操作DB就有点像假⽇往⾼速上涌⼊的车辆,只会给DB添堵。
推荐配置:20,多数场景下 20 已完全够⽤,当然这个参数跟使⽤场景相关性很⼤,⼀般配置成正常连接数的3~5倍。
DB“慢查”排查记
上⾯讲了⼀些配置的坑,那么是否中规中矩的按照推荐配置就万事⼤吉了呢,现实中的世界往往没这么简单的事,下⾯分享⼀个“慢查”排查的⼀个案例,了解⼀下DB慢查的排查思路。
有应⽤反馈发现⼤量DB慢查,并且⽇志上还记录了详细的执⾏时间和SQL语句。接到问题后我们第⼀时间排查DB发现并没有异常,也没有慢查记录,并且⽇志中的⼤部分SQL都能匹配索引,测试执⾏都在毫秒级。于是开始排查⽹络是否正常,有没丢包、重传等现象,查询监控数据发现也很正常,然后进⾏抓包分析发现实际请求处理的速度⾮常正常,⾄此可以排除DB问题。
于是再深⼊分析,查询DB其实可分为两个阶段:1. 获取连接阶段; 2. 执⾏查询阶段;绝⼤部分情况下获取连接代价⾮常⼩,直接就能从连接池获取到,即使需要新建连接代价往往也不⼤,所以使⽤时⾮常容易忽略获取连接这个阶段。什么情况下获取连接会出问题呢?⼀种情况是建⽴连接慢,⼀种是连接池已经耗尽,再对照上⾯的案例进⾏排查,依次排除了这两种情况。⾄此问题还是⼀筹莫展,还
好⾼⼿在场,想到⽤ strace 跟踪 SQL 请求前后⼲了什么,最后发现记录慢查⽇志开始和结束之间有写⽇志操作,这⾥的写⽇志是同步的并且在特定情况下正好触发了另个问题导致写⽇志⾮常慢,并且这个⽇志操作是封装在底层的,连业务开发都不清楚有这么个操作。⾄此真相⽔落⽯出,最终修复了写⽇志慢的问题后就不再出现类似的“慢查”了。
druid连接池配置详解
结语
有时⼀个“慢查”问题有时候可能并⾮像结果展⽰的那样确切,看似最可能出在DB上的问题,却是另外⼏个风马⽜不相及的原因凑到⼀起造成的,所以有时还得留个⼼眼,全局地看问题,看似⽆路可⾛时去追查抽象的背后实现。经验加演练能有效地预防故障,限于篇幅本⽂只挑选⼏个最容易引发问题及容易误解的参数做⼀些经验性地介绍,上述很多案例都可以使⽤ iptables, tc 等⼯具来模拟断⽹和丢包来复现,希望有赞的经验能帮助到读者避免⼀些常见问题。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。