数据库连接池的设计细节以及一个坑
Posted 听啃先生说H5
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库连接池的设计细节以及一个坑相关的知识,希望对你有一定的参考价值。
去年新搭建的 Web 服务,有用户反馈偶现页面打开延迟较长的情况,最初以为是用户网络问题。后来通过完善各环节日志,找到某些复现的规律
例如凌晨时打开延迟较大
程序没有抛出异常
分析各环节,最后定位在数据库连接池的设计存在缺陷。所以记录一下数据库连接池的一般实现,以及 Node.js mysql 模块连接池存在的缺陷。
一、数据库连接池设计
通过 npm install mysql 安装的 mysql 模块,包含了连接池的实现。它的设计与其他数据库驱动(比如 c 语言)类似。
初始化连接池。即创建一个空的容量有限的连接池,可理解为一个长度确定的空数组
当执行 SQL 语句的任务到来时,选出一个可用的连接对象。
SQL 语句执行过程中,执行该语句的连接对象被标志为非空闲状态
SQL 语句执行完毕,将连接对象释放,该对象变成空闲状态。
判断等待队列是否为空,空则等待新的执行任务。若非空,则表明还有 SQL 语句未执行,则重新进入第 2 个步骤,继续执行任务。
以上步骤有一个细节需要注意:任务等待队列也有容量限制,当等待执行的语句太多时,新的执行任务直接会被丢弃,并抛出执行失败的错误。
二、 Node.js mysql 模块连接池的设计缺陷
连接池在初始化时,会设置一些属性,其中包含一个获取连接对象的超时时间(acquireTimeout),这是上述 2.d 步骤中 ping 的超时时间。
当一个 socket 连接对象长时间空闲(访问量太少,没有需要执行的 SQL),这个 socket 本身可能已经损坏不可用,所以从连接池中获得的连接对象在使用前,需要 ping 一下 MySQL,确认可用再拿来执行 SQL 语句。ping 本身的通信量非常少,只是一个信号,所以成功通常非常快,几毫秒可能结束了。而失败则通常通过超时来判定。
回到本文开头的问题 —— 为什么会偶现延迟,且程序没有抛出异常?
网站刚上线访问量小,socket 连接对象长时间空闲已损坏,而 acquireTimeout 默认时间为10秒,ping 要等待超时后才去获取其它连接
根据上一节所述步骤,ping超时后,驱动程序会丢弃此损坏的连接,去获取新连接,并在新连接上执行 SQL,只要最终执行成功,就不会认为出错,只是时间稍长
三、解决办法
具体情况,具体分析,以下列出三个解决办法供参考:
初始化连接池时,设置合理的属性
acquireTimeout 设置为比较短的时间,比如正常的机房1秒足够了,如果超过1秒都不能拼通,可以认为机房有问题
假设连接池中的对象都已损坏,那连接池有N个连接,则理论上最大的延迟是 acquireTimeout的N倍,那么延迟还是有可能很大。所以将连接池大小设置为1,即只有一个连接的连接池。
连接池里的每个连接定时 ping 一下 MySQL 服务器,不要让它闲
或者干脆不要使用连接池好了,执行任务前获取连接,执行结束销毁连接
将业务发展起来,不要空闲,当然这不是技术问题了
以上是关于数据库连接池的设计细节以及一个坑的主要内容,如果未能解决你的问题,请参考以下文章