参考来源:
《极客时间-Linux性能优化实战》56 | 套路篇:优化性能问题的一般方法
从系统的角度来说,CPU、内存、磁盘和文件系统 I/O、网络以及内核数据结构等各类软硬
件资源,为应用程序提供了运行的环境,也是我们性能优化的重点对象。你可以参考咱们专
栏前面四个模块的优化篇,优化这些资源。
从应用程序的角度来说,降低 CPU 使用,减少数据访问和网络 I/O,使用缓存、异步处理
以及多进程多线程等,都是常用的性能优化方法。除了这些单机优化方法,调整应用程序的
架构,或是利用水平扩展,将任务调度到多台服务器中并行处理,也是常用的优化思路。
性能优化的方法很多,不过,我还是那句话,一定要避免过早优化。性能优化往往会提
高复杂性,这一方面降低了可维护性,另一方面也为适应复杂多变的新需求带来障碍。
CPU 性能优化的核心,在于排除所有不必要的工作、充分利用 CPU 缓存并减少进程调度对性能的
影响。
从这几个方面出发,我相信你已经想到了很多的优化方法。这里,我主要强调一下,最典型
的三种优化方法。
第一种,把进程绑定到一个或者多个 CPU 上,充分利用 CPU 缓存的本地性,并减少进
程间的相互影响。
第二种,为中断处理程序开启多 CPU 负载均衡,以便在发生大量中断时,可以充分利用
多 CPU 的优势分摊负载。
第三种,使用 Cgroups 等方法,为进程设置资源限制,避免个别进程消耗过多的 CPU。
同时,为核心应用程序设置更高的优先级,减少低优先级任务的影响。
我曾经为你梳理了常见的一些内存问题,比如可用内存不足、内存泄漏、Swap 过多、缺页异常过多以及缓存过多等等。所以,说白了,内存性能的优化,也就是要解决这些内存使用的问题。
在我看来,你可以通过以下几种方法,来优化内存的性能。
第一种,除非有必要,Swap 应该禁止掉。这样就可以避免 Swap 的额外 I/O ,带来内存访问变慢的问题。
第二种,使用 Cgroups 等方法,为进程设置内存限制。这样就可以避免个别进程消耗过多内存,而影响了其他进程。对于核心应用,还应该降低 oom_score,避免被 OOM 杀死。
第三种,使用大页、内存池等方法,减少内存的动态分配,从而减少缺页异常。
这其中有三种最典型的方法。
第一种,也是最简单的方法,通过 SSD 替代 HDD、或者使用 RAID 等方法,提升 I/O性能。
第二种,针对磁盘和应用程序 I/O 模式的特征,选择最适合的 I/O 调度算法。比如,SSD 和虚拟机中的磁盘,通常用的是 noop 调度算法;而数据库应用,更推荐使用deadline 算法。
第三,优化文件系统和磁盘的缓存、缓冲区,比如优化脏页的刷新频率、脏页限额,以及内核回收目录项缓存和索引节点缓存的倾向等等。
除此之外,使用不同磁盘隔离不同应用的数据、优化文件系统的配置选项、优化磁盘预读、增大磁盘队列长度等,也都是常用的优化思路。
这些优化方法都是从 Linux 的网络协议栈出发,针对每个协议层的工作原理进行优化。这里,我同样强调一下,最典型的几种网络优化方法。
首先,从内核资源和网络协议的角度来说,我们可以对内核选项进行优化,比如:
你可以增大套接字缓冲区、连接跟踪表、最大半连接数、最大文件描述符数、本地端口范围等内核资源配额;
也可以减少 TIMEOUT 超时时间、SYN+ACK 重传数、Keepalive 探测时间等异常处理参数;
还可以开启端口复用、反向地址校验,并调整 MTU 大小等降低内核的负担。
这些都是内核选项优化的最常见措施。
其次,从网络接口的角度来说,我们可以考虑对网络接口的功能进行优化,比如:
你可以将原来 CPU 上执行的工作,卸载到网卡中执行,即开启网卡的 GRO、GSO、RSS、VXLAN 等卸载功能;
也可以开启网络接口的多队列功能,这样,每个队列就可以用不同的中断号,调度到不同CPU 上执行;
还可以增大网络接口的缓冲区大小以及队列长度等,提升网络传输的吞吐量。
最后,在极限性能情况(比如 C10M)下,内核的网络协议栈可能是最主要的性能瓶颈,所以,一般会考虑绕过内核协议栈。
你可以使用 DPDK 技术,跳过内核协议栈,直接由用户态进程用轮询的方式,来处理网络请求。同时,再结合大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率。
你还可以使用内核自带的 XDP 技术,在网络包进入内核协议栈前,就对其进行处理。这样,也可以达到目的,获得很好的性能。
说完了系统软硬件资源的优化,接下来,我们再来看看应用程序的优化思路。
虽然系统的软硬件资源,是保证应用程序正常运行的基础,但你要知道,性能优化的最佳位置,还是应用程序内部。
第一个例子,是系统 CPU 使用率(sys%)过高的问题。有时候出现问题,虽然表面现象是系统 CPU 使用率过高,但待你分析过后,很可能会发现,应用程序的不合理系统调用才是罪魁祸首。这种情况下,优化应用程序内部系统调用的逻辑,显然要比优化内核要简单也有
用得多。
再比如说,数据库的 CPU 使用率高、I/O 响应慢,也是最常见的一种性能问题。这种问题,一般来说,并不是因为数据库本身性能不好,而是应用程序不合理的表结构或者 SQL查询语句导致的。这时候,优化应用程序中数据库表结构的逻辑或者 SQL 语句,显然要比
优化数据库本身,能带来更大的收益。
所以,在观察性能指标时,你应该先查看应用程序的响应时间、吞吐量以及错误率等指标,因为它们才是性能优化要解决的终极问题。
以终为始,从这些角度出发,你一定能想到很多优化方法,而我比较推荐下面几种方法。
第一,从 CPU 使用的角度来说,简化代码、优化算法、异步处理以及编译器优化等,都
是常用的降低 CPU 使用率的方法,这样可以利用有限的 CPU 处理更多的请求。
第二,从数据访问的角度来说,使用缓存、写时复制、增加 I/O 尺寸等,都是常用的减
少磁盘 I/O 的方法,这样可以获得更快的数据处理速度。
第三,从内存管理的角度来说,使用大页、内存池等方法,可以预先分配内存,减少内存
的动态分配,从而更好地内存访问性能。
第四,从网络的角度来说,使用 I/O 多路复用、长连接代替短连接、DNS 缓存等方法,
可以优化网络 I/O 并减少网络请求数,从而减少网络延时带来的性能问题。
第五,从进程的工作模型来说,异步处理、多线程或多进程等,可以充分利用每一个
CPU 的处理能力,从而提高应用程序的吞吐能力。
除此之外,你还可以使用消息队列、CDN、负载均衡等各种方法,来优化应用程序的架构,将原来单机要承担的任务,调度到多台服务器中并行处理。这样也往往能获得更好的整体性能。