数据库连接池到底应该设多大?这里面有大学问
作者:kelgon
www.jianshu.com/p/a8f653fc0c54
我在研究HikariCP(一个数据库连接池)时无意间在HikariCP的Github wiki上看到了一篇文章,这篇文章有力地消除了我一直以来的疑虑,看完之后感觉神清气爽。故在此做译文分享。
文章链接:https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
接下来是正文
1万并发用户访问
想象你有一个网站,压力虽然还没到Facebook那个级别,但也有个1万上下的并发访问——也就是说差不多2万左右的TPS。那么这个网站的数据库连接池应该设置成多大呢?结果可能会让你惊讶,因为这个问题的正确问法是:
下面这个视频是Oracle Real World Performance Group发布的,请先看完:
http://www.dailymotion.com/video/x2s8uec
视频中对Oracle数据库进行压力测试,9600并发线程进行数据库操作,每两次访问数据库的操作之间sleep 550ms,一开始设置的中间件线程池大小为2048:
每个请求要在连接池队列里等待33ms,获得连接后执行SQL需要77ms
此时数据库的等待事件是这个熊样的:
各种buffer busy waits,数据库CPU在95%左右(这张图里没截到CPU)
获取链接等待时长没怎么变,但是执行SQL的耗时减少了。
下面这张图,上半部分是wait,下半部分是吞吐量
能看到,中间件连接池从2048减半之后,吐吞量没变,但wait事件减少了一半。
接下来,把数据库连接池减到96,并发线程数仍然是9600不变。
队列平均等待1ms,执行SQL平均耗时2ms。
wait事件几乎没了,吞吐量上升。
没有调整任何其他东西,仅仅只是缩小了中间件层的数据库连接池,就把请求响应时间从100ms左右缩短到了3ms。
But why?
为什么nginx只用4个线程发挥出的性能就大大超越了100个进程的Apache HTTPD?回想一下计算机科学的基础知识,答案其实是很明显的。
即使是单核CPU的计算机也能“同时”运行数百个线程。但我们都[应该]知道这只不过是操作系统用时间分片玩的一个小把戏。一颗CPU核心同一时刻只能执行一个线程,然后操作系统切换上下文,核心开始执行另一个线程的代码,以此类推。给定一颗CPU核心,其顺序执行A和B永远比通过时间分片“同时”执行A和B要快,这是一条计算机科学的基本法则。一旦线程的数量超过了CPU核心的数量,再增加线程数系统就只会更慢,而不是更快。
这几乎就是真理了……
有限的资源
网络和磁盘类似。通过以太网接口读写数据时也会形成阻塞,10G带宽会比1G带宽的阻塞少一些,1G带宽又会比100M带宽的阻塞少一些。不过网络通常是放在第三位考虑的,有些人会在性能计算中忽略它们。
上图是PostgreSQL的benchmark数据,可以看到TPS增长率从50个连接数开始变缓。在上面Oracle的视频中,他们把连接数从2048降到了96,实际上96都太高了,除非服务器有16或32颗核心。
计算公式
下面的公式是由PostgreSQL提供的,不过我们认为可以广泛地应用于大多数数据库产品。你应该模拟预期的访问量,并从这一公式开始测试你的应用,寻找最合适的连接数值。
核心数不应包含超线程(hyper thread),即使打开了hyperthreading也是。如果活跃数据全部被缓存了,那么有效磁盘数是0,随着缓存命中率的下降,有效磁盘数逐渐趋近于实际的磁盘数。这一公式作用于SSD时的效果如何尚未有分析。
笔者注:
这一公式其实不仅适用于数据库连接池的计算,大部分涉及计算和I/O的程序,线程数的设置都可以参考这一公式。我之前在对一个使用Netty编写的消息收发服务进行压力测试时,最终测出的最佳线程数就刚好是CPU核心数的一倍。
公理:你需要一个小连接池,和一个充满了等待连接的线程的队列
如果你有10000个并发用户,设置一个10000的连接池基本等于失了智。1000仍然很恐怖。即是100也太多了。你需要一个10来个连接的小连接池,然后让剩下的业务线程都在队列里等待。连接池中的连接数量应该等于你的数据库能够有效同时进行的查询任务数(通常不会高于2*CPU核心数)。
我们经常见到一些小规模的web应用,应付着大约十来个的并发用户,却使用着一个100连接数的连接池。这会对你的数据库造成极其不必要的负担。
请注意
1.不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事
4.“37岁,985毕业,年薪50万,被裁掉只用了10分钟”
5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...