当前位置: 首页 > 科技观察

数据库连接池应该有多大?

时间:2023-03-20 15:31:33 科技观察

在研究HikariCP(一个数据库连接池)的时候,无意中看到了HikariCP的Githubwiki上的一篇文章。这篇文章有效地打消了我长久以来的疑惑,读后感觉耳目一新。所以在这里分享翻译。文章链接:https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing接下来是文本数据库连接池的配置,是开发者经常犯的坑。在配置数据库连接池的时候,有几个可以说是违反直觉的原则需要弄清楚。10,000个并发用户访问假设您有一个网站。虽然压力没有达到Facebook的水平,但也有10000左右的并发访问——也就是20000TPS左右。那么这个网站的数据库连接池应该设置多大呢?结果可能会让你大吃一惊,因为问这个问题的正确方式是:“这个网站的数据库连接池应该设置多小?”以下视频由OracleRealWorldPerformanceGroup发布,请先观看:http://www.dailymotion.com/video/x2s8uec因为这个视频是英文的,没有字幕,所以我给大家做一个简单的总结:视频中对Oracle数据库进行了压力测试,使用9600个并发线程进行数据库操作。每两次访问数据库操作之间的sleep为550ms,一开始设置的中间件线程池大小为2048:压测运行后是这样的:每个请求需要在连接池队列中等待33ms,并且得到连接后执行SQL需要77ms数据库的等待事件是这样的:各种buffer忙等待,数据库CPU占用95%左右(这张图中没有抓到CPU)接下来减少中间件连接池到1024(并发数没变),性能数据变成了这样:获取连接的等待时间没有太大变化,但是执行SQL++++++++++++++++++++的耗时减少了。下图中,上半部分是wait,下半部分是throughput。可以看到,中间件连接池从2048减半后,吞吐量没有变化,但是等待事件减少了一半。接下来,将数据库连接池减少到96,并发线程数保持9600不变,队列平均等待1ms,执行SQL的平均时间为2ms。等待事件几乎没有了,吞吐量上去了。在不做任何调整的情况下,仅仅缩小中间件层的数据库连接池就可以将请求响应时间从100ms左右缩短到3ms左右。但为什么?为什么只有4个线程的nginx性能就大大超过了100个进程的ApacheHTTPD?回顾一下计算机科学的基础知识,答案其实很明显。即使是只有一个CPU内核的计算机也可以“同时”运行数百个线程。但我们都[应该]知道,这只是操作系统在时间分片上玩的一个把戏。一个CPU内核一次只能执行一个线程,然后操作系统切换上下文,内核开始执行另一个线程的代码,如此往复。给定一个CPU核心,顺序执行A和B总是比通过时间片“同时”执行A和B更快。这是计算机科学的一个基本规律。一旦线程数超过CPU核心数,增加线程数只会让系统变慢,不会变快。建议:多线程内容聚合这个差不多是真理吧。。。资源有限上面的说法只能说是接近真理,其实并没有那么简单,还有一些其他的因素需要补充。当我们寻找数据库性能瓶颈时,我们总是可以将它们分为三类:CPU、磁盘、网络。加内存没有错,但是相对于磁盘和网络,内存的带宽要高几个数量级,所以就不加了。如果我们忽略磁盘和网络,结论很简单。在8核服务器上,将连接数/线程数设置为8可以提供最佳性能,增加连接数会因为失去上下文切换而导致性能下降。数据库通常将数据存储在磁盘上,磁盘通常由旋转的金属盘片和安装在步进电机上的读/写磁头组成。读/写磁头一次只能在一个地方,然后它必须“寻址”到另一个位置才能执行另一次读或写操作。因此,就存在着寻址的耗时,除了旋转耗时之外,读写头还需要等待盘片上的目标数据“旋转到位”后才能进行操作。使用缓存当然可以提高性能,但以上原则仍然成立。在此时间段(“I/O等待”)期间,线程被“阻塞”等待磁盘,操作系统可以使用空闲的CPU核心来为其他线程服务。因此,由于线程总是阻塞在I/O上,我们可以拥有比CPU内核更多的线程/连接,这样就可以在相同的时间内完成更多的工作。那么应该是多少呢?这取决于磁盘。较新的SSD不需要寻址,也没有旋转盘片。不要想当然地认为“SSD更快,所以我们应该增加线程数”,相反,不需要寻址,没有迂回耗时意味着更少的阻塞,所以更少的线程[更接近于线程数]CPU内核]将表现出更高的性能。只有阻塞创造了更多的执行机会,更多的线程才能获得更好的性能。网络和磁盘是相似的。通过以太网接口读写数据时,也会发生阻塞。10G带宽比1G带宽阻塞少,1G带宽比100M带宽阻塞少。网络通常是第三个考虑因素,有些人在性能计算中忽略它们。上图是PostgreSQL的benchmark数据。可以看出,TPS增长速度从50个连接开始放缓。在上面的Oracle视频中,他们将连接数从2048减少到96,这实际上太高了,除非服务器有16或32核。计算公式下面的公式是PostgreSQL提供的,但我们认为它可以广泛应用于大多数数据库产品。您应该模拟预期的流量并从这个公式开始测试您的应用程序以找到最合适的连接值。NumberofConnections=((NumberofCores*2)+NumberofEffectiveDisks)核心数不应该包含超线程,即使开启了超线程。如果缓存了所有活动数据,则有效磁盘数为0。随着缓存命中率的降低,有效磁盘数逐渐接近实际磁盘数。这个公式对SSD的影响还没有分析。根据这个公式,你的4核i7数据库服务器的连接池大小应该是((4*2)+1)=9,四舍五入到10,是不是太小了?尝试运行性能测试,我们保证它可以轻松应对3000个用户以6000TPS的速率并发执行简单查询的场景。如果连接池大小超过10,您将看到响应时间开始增加并且TPS开始下降。延伸:你知道用了这么久的数据库连接池的原理吗?作者注:这个公式不仅仅适用于数据库连接池的计算,大部分涉及计算和I/O的程序,线程数的设置都可以参考这个公式。当我在压力测试一个用Netty写的消息服务时,我最终测量到的最佳线程数恰好是CPU核数的两倍。公理:你需要一个小的连接池,以及一个充满等待连接的线程的队列。如果你有10000个并发用户,设置一个10000个连接池,基本上就等于失去了理智。1000还是吓人的。即使是100也太多了。你需要一个10个左右连接的小连接池,然后让剩下的业务线程在队列中等待。连接池中的连接数应该等于你的数据库可以同时有效执行的查询任务数(通常不高于2*CPU核心数)。我们经常看到一些小型Web应用程序,处理大约十几个并发用户,但使用100个连接的连接池。这会给您的数据库带来极其不必要的负担。请注意,连接池的大小最终与系统特性有关。例如,混合长事务和短事务的系统通常很难对任何连接池进行调优。最好的方法是创建两个连接池,一个用于长事务,一个用于短事务。再比如,系统执行一个任务队列,只允许同时执行一定数量的任务。这时候并发任务数应该和连接池中的连接数相适应,而不是相反。