文章目录
- 服务器性能优化方法
- 什么是服务器并发处理能力?
- 什么方法衡量服务器的并发能力?
- 怎么提高服务器的并发处理能力?
- **1,提高CPU并发计算能力**
- (1)多进程&多线程
- (2)减少进程切换,使用线程,考虑进程绑定CPU
- (3)减少使用不必要的锁,考虑无锁编程
- (4)考虑进程优先级
- (5)关注系统负载
- (6)关注CPU使用率,除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait
- **2,减少系统调用**
- **3,考虑减少内存分配和释放**
- **4**,考虑使用持久连接
- **5**,改进I/O模型
- **6**,改进服务器并发策略
- **7**,改进硬件环境
- 压力测试
- 压力测试提前考虑的条件
- **压力测试中关系的时间有细分以下**2种,
服务器性能优化方法
什么是服务器并发处理能力?
一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强。
服务器的本质工作就是,争取以最快的速度将内核缓冲区中的用户请求数据一个不剩地都拿出来,然后尽快处理,再将响应数据放到一块又能够与发送数据的缓冲区中,接着处理下一拨请求。
什么方法衡量服务器的并发能力?
衡量服务器并发处理能力的方法有很多,以下是一些常见的方法:
- TPS(Transactions Per Second):每秒事务数,即服务器每秒钟能够处理多少个请求。TPS是衡量服务器性能最常用的指标之一。
- QPS(Queries Per Second):每秒查询数,与TPS类似,但更关注数据库查询的数量。
- RPS(Requests Per Second):每秒请求数,是服务器最直接的衡量标准,它可以度量所有类型的请求,包括静态和动态内容。
- 响应时间:服务器对请求做出响应的时间,通常使用平均值或中位数来衡量。
- 吞吐量:服务器在一定时间内处理的请求数量,通常使用总请求数量除以总处理时间来计算。
- 并发连接数:同时连接到服务器的客户端数量,通常使用峰值并发连接数来衡量。
需要注意的是,这些指标都只是衡量服务器并发处理能力的一部分,不同的应用场景可能需要侧重不同的指标。
怎么提高服务器的并发处理能力?
1,提高CPU并发计算能力
服务器之所以可以同时处理多个请求,在于操作系统通过多执行流体系设计使得多个任务可以轮流使用系统资源,这些资源包括CPU,内存以及I/O. 这里的I/O主要指磁盘I/O, 和网络I/O
(1)多进程&多线程
多执行流的一般实现就是进程。多进程的好处不仅在于CPU时间的轮流使用,还在于对CPU计算和I/O操作进行很好的重叠利用,这里的I/O主要指磁盘I/O和网络I/O. 实际上,大多数进程的时间主要消耗在I/O操作上,现代计算机的DMA技术可以让CPU不参与I/O操作的全过程,比如进程通过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,然后进程被挂起,释放出CPU资源,等待I/O设备完成工作后通过中断来通知进程重新就绪。对于单任务而言,CPU大部分时间空闲,这时候多进程的作用尤为重要。
而且进程的优越性还在其相互独立带来的稳定性和健壮性方面。
进程的缺点: 每个进程都有自己的独立空间和生命周期。当子进程被父进程创建后,便将父进程地址空间的所有数据复制到自己的地址空间中,完全继承父进程的上下文信息。进程的创建使用fork()系统调用,还是有一定的开销的,这个开销若太频繁,其可能成为影响性能的主要因素。
(2)减少进程切换,使用线程,考虑进程绑定CPU
进程拥有独立的内存空间,每个进程都只能共享CPU寄存器。一个进程被挂起的本质是将它在CPU寄存器中的数据拿出来暂存在内存态堆栈着那个,而一个进程恢复工作的本质就是把它的数据重新装入CPU寄存器,这段装入和移出的数据称为“硬件上下文”,除此之外,进程上下文还包含进程允许所需的一切状态信息。
当硬件上下文频繁装入和移出时,所消耗的时间是非常可观的。可用Nmon工具监视服务器每秒的上下文切换次数。
为了尽量减少上下文切换次数,最简单的做法就是减少进程数,尽量使用线程并配合其它I/O模型来设计并发策略。
还可以考虑使用进程绑定CPU技术,增加CPU缓存的命中率。若进程不断在各CPU上切换,这样旧的CPU缓存就会失效。
(3)减少使用不必要的锁,考虑无锁编程
服务器处理大量并发请求时,多个请求处理任务时存在一些资源抢占竞争,这时一般采用“锁”机制来控制资源的占用,当一个任务占用资源时,我们锁住资源,这时其它任务都在等待锁的释放,这个现象称为锁竞争。
通过锁竞争的本质,我们要意识到尽量减少并发请求对于共享资源的竞争。比如在允许情况下关闭服务器访问日志,这可以大大减少在锁等待时的延迟时间。要最大程度减少无辜的等待时间。
这里说下无锁编程,就是由内核完成这个锁机制,主要是使用原子操作替代锁来实现对共享资源的访问保护 ,使用原子操作时,在进行实际的写操作时,使用了lock指令,这样就可以阻止其他任务写这块内存,避免出现数据竞争现象。原子操作速度比锁快,一般要快一倍以上。
例如fwrite(), fopen(),其是使用append方式写文件,其原理就是使用了无锁编程,无锁编程的复杂度高,但是效率快,而且发生死锁概率低。
(4)考虑进程优先级
进程调度器会动态调整运行队列中进程的优先级,通过top观察进程的PR值
(5)关注系统负载
可在任何时刻查看/proc/loadavg, top中的load average也可看出
(6)关注CPU使用率,除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait
它是指CPU空闲并且等待I/O操作完成的时间比例。(top中查看wa的值)
2,减少系统调用
进程若运行在用户态,这时可使用CPU和内存来完成一些任务,而当进程需要对硬件外设进行操作的时候(如读取磁盘文件,发送网络数据等),就必须切换到内核态,这时它拥有更多的权力来操纵整个计算机。
而系统调用涉及进程从用户态到内核态的切换,导致一定的内存交换,这也是一定程度上的上下文切换,所以系统调用的开销通常认为比较昂贵的。
所以要减少不必要的系统调用,也是服务器性能优化的一个方面。例如在apache中,修改httpd.conf文件,可以减少对文件路径中各级目录下检测是否存在.htacess文件这个open()系统调用; 还可以修改httpd.conf文件来减少多余的gettimeofday()系统调用。
但有时若使用一些简单的系统调用能代替大量的逻辑运算,这样反而使用系统调用更能优化性能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMT0uxMB-1685626181453)
3,考虑减少内存分配和释放
(1)改善数据结构和算法复制度
(2)使用内存池
(3)考虑使用共享内存
4,考虑使用持久连接
持久连接也为长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多分数据而不断开连接,与它相反的方式称为短连接,也就是建立连接后发送一份数据就断开,然后再次建立连接发送下一份数据, 周而复始。是否采用持久连接,完全取决于应用特点。从性能角度看,建立TCP连接的操作本身是一项不小的开销,在允许的情况下,连接次数越少,越有利于性能的提升; 尤其对于密集型的图片或网页等小数据请求处理有明显的加速所用。
HTTP长连接需要浏览器和web服务器的共同协作,目前浏览器普遍支持长连接,表现在其发出的HTTP请求数据头中包含关于长连接的声明,如下: Connection: Keep-Alive
主流的web服务器都支持长连接,比如apache中,可以用KeepAlive off关闭长连接。
对于长连接的有效使用,还有关键一点在于长连接超时时间的设置,即长连接在什么时候关闭吗? Apache的默认设置为5s, 若这个时间设置过长,则可能导致资源无效占有,维持大量空闲进程,影响服务器性能。
5,改进I/O模型
(1)DMA技术
(2)异步I/O
(3)改进多路I/O就绪通知策略,epoll
(4)Sendfile
(5)内存映射
(6)直接I/O
I/O操作根据设备的不同分为很多类型,比如内存I/O, 网络I/O, 磁盘I/O. 对于网络I/O和磁盘I/O, 它们的速度要慢很多,尽管使用RAID磁盘阵列可通过并行磁盘磁盘来加快磁盘I/O速度,购买大连独享网络带宽以及使用高带宽网络适配器可以提高网络i/O的速度。但这些I/O操作需要内核系统调用来完成,这些需要CPU来调度,这使得CPU不得不浪费宝贵的时间来等待慢速I/O操作我们希望让CPU足够少的时间在i/O操作的调度上,如何让高速的CPU和慢速的I/O设备更好地协调工作,是现代计算机一直探讨的话题。各种I/O模型的本质区别在于CPU的参与方式。
(1)DMA技术I/O设备和内存之间的数据传输方式由DMA控制器完成。在DMA模式下,CPU只需向DMA下达命令,让DMA控制器来处理数据的传送,这样可以大大节省系统资源。
(2)异步I/O
异步I/O指主动请求数据后便可以继续处理其它任务,随后等待I/O操作的通知,这样进程在数据读写时不发生阻塞。而同步则在数据就绪后在读写时必须阻塞。
异步I/O是非阻塞的,当函数返回时,真正的I/O传输还没开始,这让CPU处理和I/O操作达到很好的重叠。
顺便说说同步阻塞I/O的缺点,其虽然可以和多进程有效利用CPU资源,但代价是占用了大量的内存开销。而同步非阻塞I/O需要进程执行多次轮训查看数据是否就绪,花费了大量的CPU时间。
(3)改进多路I/O就绪通知策略,epoll服务器同时处理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同时接收TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,不管这些socket有没有可接收的数据,都要询问一次,假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket.有没有可以接收的数据, 多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。
下面详细介绍被公认为linux 2.6下性能最好的多路I/O就绪通知方法epoll., 几乎具备select, poll, /dev/poll等模型的全部优点
epoll可以同时支持水平触发和边缘触发,理论上边缘触发性能更高,但是代码实现复杂,因为任何意外的丢失事件都会造成请求处理错误。
epoll主要有2大改进:
(1)epoll只告知就绪的文件描述符,而且当调用epoll_wait()获得文件描述符时,返回并不是实际的描述符,而是一个代表就绪描述符数量的值,然后只需去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里使用了内存映射(mmap)技术,这样彻底省掉了这些文件描述符在系统调用时复制的开销。
(2)epoll采用基于事件的就绪通知方式。其事先通过epoll_ctrl()注册每一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似callback的回调机制,当进程调用epoll_wait()时得到通知
(4)Sendfile
大多数时候,我们都向服务器请求静态文件,比如图片,样式表等,在处理这些请求时,磁盘文件的数据先经过内核缓冲区,然后到用户内存空间,不需经过任何处理,其又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。
Linux提供sendfile()系统调用,可以讲磁盘文件的特定部分直接传送到代表客户端的socket描述符,加快了静态文件的请求速度,同时减少CPU和内存的开销。
适用场景: 对于请求较小的静态文件,sendfile发挥的作用不那么明显,因发送数据的环节在整个过程中所占时间的比例相比于大文件请求时小很多。
(5)内存映射
Linux内核提供一种访问磁盘文件的特殊方式,它可以将内存中某块地址空间和我们指定的磁盘文件相关联,从而对这块内存的访问转换为对磁盘文件的访问。这种技术称为内存映射。
多数情况下,内存映射可以提高磁盘I/O的性能,无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样自由访问文件。
缺点:在处理较大文件时,内存映射会导致较大的内存开销,得不偿失。
(6)直接I/O
在linux 2.6中,内存映射和直接访问文件没有本质差异,因为数据需要经过2次复制,即在磁盘与内核缓冲区之间以及在内核缓冲区与用户态内存空间。
引入内核缓冲区的目的在于提高磁盘文件的访问性能,然而对于一些复杂的应用,比如数据库服务器,它们为了进一步提高性能,希望绕过内核缓冲区,由自己在用户态空间实现并管理I/O缓冲区,比如数据库可根据更加合理的策略来提高查询缓存命中率。另一方面,绕过内核缓冲区也可以减少系统内存的开销,因内核缓冲区本身就在使用系统内存。
Linux在open()系统调用中增加参数选项O_DIRECT,即可绕过内核缓冲区直接访问文件,实现直接I/O。
在Mysql中,对于Innodb存储引擎,自身进行数据和索引的缓存管理,可在my.cnf配置中分配raw分区跳过内核缓冲区,实现直接I./O
6,改进服务器并发策略
服务器并发策略的目的,是让I/O操作和CPU计算尽量重叠进行,一方面让CPU在I/O等待时不要空闲,另一方面让CPU在I/O调度上尽量花最少的时间。
(1)一个进程处理一个连接,非阻塞I/O,使用长连接
Apache使用这个模型,其进程的开销限制了它的并发连接数,但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响Apache本身,Apache父进程可以创建新的子进程;另一方面,Apache经过长期的考验和广发的使用,功能模块非常丰富。所以对于一些并发数要求不高(如150以内),还对其它功能有依赖,那么可考虑Apache这个模型。
(2)一个进程处理多个连接,异步I/O, 使用长连接
一个进程处理多个连接,潜在条件就是多路I/O就绪通知的应用。
服务器通常维护者大量的空闲连接,有些可能由于使用长连接而在等待超时,有些可能是网络传输的延时等等,这时epoll只会关注活跃连接,而不在死连接上浪费时间,但是select和poll会扫描所有文件描述符,这个是个非常昂贵的开销。一个典型的应用就是图片服务器,它们希望为用户提供网页中大量图片的快速下载,采用长连接,但是这些大量连接在等待超时关闭前,处于空闲状态,这种情况下,epoll依然能很好工作。
POSIX的标准库(aio.h)中定义了AIO的一系列接口,它几乎屏蔽了一切网络通信的细节,对使用者而言非常简单。AIO没有提供非阻塞的open()方法,进程仍使用open()系统调用来打开文件,然后填充一些I/O请求的数据结构(struct aiocb),接下来调用aid_read()或aid_write()来发起异步I/O操作,一旦请求进入操作队列后,函数便返回,进程可以在此调用aid_error()来检查正在运行的I/O操作的状态
7,改进硬件环境
压力测试
压力测试是一种软件测试方法,用于评估系统在不同负载条件下的性能表现。在压力测试中,会模拟出大量并发用户访问系统、发送请求或执行操作的情况,以测试系统是否可以正常运行,并确定其响应时间、吞吐量、稳定性和可靠性等性能指标。通过压力测试,可以发现系统存在的瓶颈和性能问题,帮助开发人员优化系统设计、提高系统性能和稳定性。
有一个原理一定要先搞清楚,假如100个用户同时向服务器分别进行10个请求,与1个用户向服务器连续进行1000次请求,对服务器的压力是一样吗?实际上是不一样的,因对每一个用户,连续发送请求实际上是指发送一个请求并接收到响应数据后再发送下一个请求。这样对于1个用户向服务器连续进行1000次请求, 任何时刻服务器的网卡接收缓冲区中只有1个请求,而对于100个用户同时向服务器分别进行10个请求,服务器的网卡接收缓冲区最多有100个等待处理的请求,显然这时的服务器压力更大。
压力测试提前考虑的条件
(1) 并发用户数
(2) 总请求数
(3) 请求资源描述
压力测试中关系的时间有细分以下2种,
(1) 用户平均请求等待时间
(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)
(2) 服务器平均请求处理时间