性能问题之数据库连接池

案例

压测过程中,某个接口,分别用10/20/30/40个线程进行测试,结果如下:

从上述结果中可以看出,线程数从20-40,TPS基本没有提高,应用服务器和数据库服务器的CPU使用率也基本没有提高。

在用40线程进行测试时,用jstack pid > ××.log ,将堆栈日志文件下载到本地,分别在日志中查找BLOCKED、TIME_WAITING状态的线程。

BLOCKED状态的线程不存在,TIME_WAITING状态数据如下,基本判断是由数据库连接池不够用引起的问题。

将java程序的配置文件中的spring.datasource.druid.max-active改成50,重启应用程序后在重新测试。

 

数据库连接

1、简介
应用程序在做数据操作之前,首先要和数据库建立连接。但是数据库链接频繁的创建、销毁是会带来性能的下降。为解决上述问题,可以采用数据库连接池技术。
数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。数据库连接池的意义在于,能够重复利用数据库连接,提高对请求的响应时间和服务器的性能。
 池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低 系统资源开销。
 
2、数据库监控连接
命令如下:
show status where Variable_name = 'Threads_connected' ; 
show variables where Variable_name="max_connections";

其中,Threads_connected是当前打开的连接的数量,max_connections是mysql设置的最大连接数。

 

3、连接池原理

目前行业内常用的连接池有C3P0、DBCP、druid等,连接池通常会有以下几个重要参数:
  • initialSize 初始化时建立物理连接的个数 
  • minIdle 最小连接池数量
  • maxActive 连接池最大连接数量
  • maxWait 获取连接时最大等待时间,单位毫秒

4、配置建议
initialSize:对于db规模特别大的情况下可以考虑设置为1个。避免启动时间过长,以及没有业务时占用过多的数据库连接资源
minIdle:可考虑该值的设置和初始化连接保持一致
maxActive:连接池最大连接数量,最大连接不要设置过大,避免本地维护的db太大。如果对应到数据源的并发数过高,可考虑增大最大连接数。一般设置20-50比较合理
maxWait:获取连接时最大等待时间,如果连接全部被占用,需要等待的时间。可以根据当前系统的响应时间判定,如果容忍度较高,可以大点。容忍度较低,设置小点。一般设置在5000-10000.
 
Tomcat连接
1、原理简介
Tomcat处理时,是需要Connector进行调度和控制的,Connector是Tomcat处理请求的主干。
Connector 中有一个 accept 队列,当客户端向服务器发送请求时,如果客户端与操作系统完 成三次握手建立了连接,就将该连接放入 accept 队列,poller 从队列中获取到链接后,从链 接中获取请求,生成一个 request,然后从 tomcat 内部的线程池中找到空闲的线程去执行 对应请求。

2、参数设置

tomcat中相关参数如下图:

  • acceptCount :accept队列的长度; 当 accept 队列中连接的个数达到 acceptCount 时,队列满,进来的请 求一律被拒绝。默认值是 100。
  • maxConnections:Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections 时,Acceptor 不会读取 accept 队列中的连接;这时 accept 队列中的线程会一直阻塞着,直 到 Tomcat 接收的连接数小于 maxConnections。如果设置为-1,则连接数不受限制。 默认值与连接器使用的协议有关:NIO 的默认值是 10000,APR/native 的默认值是 8192.
  • maxThreads:内部线程池请求处理线程的最大数量。默认值是200(Tomcat7和Tomcat8都是)。maxThreads规定的是最大的线程数目,maxThreads的大小比CPU核心数量要大得多。这是因为处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。因此,在同一时刻,只有少数的线程真正的在使用物理CPU,大多数线程都在等待;因此线程数远大于物理核心数才是合理的。换句话说,Tomcat通过使用比CPU核心数量多得多的线程数,可以使CPU忙碌起来,大大提高CPU的利用率。 当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量时间用不线程的切换,整体效率会降低。

maxThreads的设置既与应用的特点有关,也与服务器的CPU核心数量有关。通过前面的介绍可以知道,maxThreads的数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。一般在企业中常见的配置数量在500-1000之间。

通过前面的设置可以知道,虽然tomcat同时可以处理的连接数目是maxConnections,但服务器中可以同时接收的连接数为maxConnections+acceptCount。acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系,如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。
 
连接和连接池相关
1、操作系统是通过一 个四元组来标识一个 TCP 链接:{本地 ip,本地 port,远程 ip,远程 port}。这四个要素唯一确定一个 TCP 链接,任意一个要素不相同,就认为是一个不同的链接。
在 Linux 系统中,一切皆文件,每一个 TCP 链接都要占用一个文件句柄,系统允许创建的 链接数取决于句柄数的上限。超过这个值再创建链接就会报这样的错误:“Can't open so many files" 
通过命令 ulimit -n 可以查看当前系统允许打开文件数量的上限,在 Linux 中这个值默认是 1024,也就是说默认情况下,只能创建 1024 个链接。
同时这个值也是可以修改的,通过 修改/etc/security/limits.conf 文件,可以把这个值改大,一般服务器都会改的很大,比如我 们的服务器上一般设置为 1000000。
那这么说是不是就意味着只要我改的很大,链接数可以无限大了?
其实也并不是这样,创建链接的时候,一般分为两个端,即链接的发起端和链接接收端。 
 
2、链接发起端 :对于 Jmeter 来说,它是链接发起端,Jmeter 创建了 5 个链接去连接服务端的 8080 端口, 每个新建链接会占用了一个端口号,如图中的 10001-10005。在操作系统中,端口号的范围是0-65535,其中0-1024是预留端口号,不可使用,其他的端口是可以使用的。也就是说,在链接发起端,受端口号的限制理论上最多可以创建64000左右链接。
 
3、链接接收端:对于 Tomcat 服务器来讲,它是链接接收端,它是不是也受限于 65535 呢?并不是,从上 面图中可以看到,Jmeter 发起的所有链接都创建在 Tomcat 服务器的 8080 端口,也就是说 对于链接接收端,所有的链接占用的是同一个端口。根据 TCP 标识四元组可以分析出,一 个链接接收端,最大的 TCP 链接数=所有有效 ip 排列组合的数量*端口数量 65535,这个计 算结果应该是一个天文数字。因此链接接收端支持的链接数理论上可以认为是无限大的。
 
4、上面介绍的一些数据都是理论上单台机器可以支持的 TCP 链接数,实际情况下,每创建一 个链接需要消耗一定的内存,大概是 4-10kb,所以链接数也受限于机器的总内存。
 
posted @ 2022-05-08 16:43  阳光倾林  阅读(701)  评论(0编辑  收藏  举报