1.性能问题理论综述
    系统的性能是指操作系统完成任务的有效性、稳定性和响应速度
    linux系统管理员可能会经常遇到系统不稳定、响应速度慢等问题,例如在linxu上搭建了一个web服务,经常会出现网页无法打开、打开速度慢等现象,而遇到这些问题,就会有人抱怨linux系统不好,其实这些都是表面现象。操作系统完成一个任务时,与系统自身设置、网络拓扑结构、路由设备、路由策略、接入设备、物理线路的多个方面都密切相关,任何一个环节出现问题,都会影响整个系统的性能。因此当linux应用出现问题是,应当重应用程序、操作系统、服务器硬件、网络环境等方面综合排查,定位问题出现在哪个部分,然后集中解决。
    在应用程序、操作系统、硬件服务、网络环境等方面,影响性能最大的是应用程序和操作系统两个方面,应为这两个方面出现问题不易察觉,隐蔽性很强。而硬件、网络方面只要出现问题,一般都能马上定位。下面主要讲解操作系统方面的性能调优思路,应用程序方面需要具体问题具体对待。
    2.系统硬件资源
    2.1 cpu
    cpu是操作系统稳定运行的根本,cpu的速度与性能在很大程度上决定了系统整体的性能,因此理论上cpu数量越多、主频越高,服务器的性能也就相对越好。但是事实并非如此。
    目前大部分的cpu在同一时间段内只能运行一个线程,超线程的处理器可以在同一时间运行多个线程,因此可以利用处理器的超线程特性提高系统性能。在linux系统下,只有运行SMP内核才能支持超线程,但是,安装的cpu的数量越多,从超线程获得的性能方面的提高就越少。另外,linux内核会把多核的处理器当做单独的cpu来识别,例如两个4和cpu在linux系统下就会被当做8个单核cpu。但是从性能角度来讲,2个四核的cpu和8个单核的cpu并不完全等价,根据权威部分测试得出结论,前者的整体性能要比后者低25%~30%。
    可能出现cpu瓶颈的应用有db服务器、动态web服务器等,对于这类应用,要把cpu的配置和性能放在主要位置
    2.2 内存
    内存的大小也是影响Linux性能的一个重要因素,内存太小,系统进程将被阻塞,应用也将变得缓慢,甚至失去响应;内存太大,导致资源浪费。Linux系统采用了物理内存和虚拟内存(虚拟内存大数据中避免使用)两种方式,虚拟内存虽然可以缓解内存不足,但是占用过多的虚拟内存,应用程序的性能明显下降,要保证应用程序的高性能的运行,物理内存一定要足够大;但是过大的物理内存,会造成内存资源的浪费,例如,在一个32位处理器的linux操作系统上,超过8G的物理内存都将被浪费。因此,要是用更大的内存,建议安装64位操作系统,同时开启Linux的大内存支持。
    犹豫处理器寻址范围的限制,在32位Linux操作系统上,应用程序单个进程最大只能使用4GB的内存,这样一来,即使系统有更大的内存,应用程序也无法‘享’用。解决办法就是使用64位处理器,安装64位操作系统。在64位操作系统下,可以满足所有程序对内存使用的需求,几乎没有限制。
    可能出现内存性能瓶颈的应用有NOSQL(非关系型的数据库)服务器,数据库服务器、缓存服务器等,对于这类应用要把内存大小放在主要位置
    2.3磁盘I/O性能
    磁盘的I/O性能直接影响应用程序的性能,在一个有频繁读写的应用中,如果磁盘I/O性能得不到满足,就会导致应用停滞。好在现今的磁盘都才用了很多方法来提高I/O性能,比如常见的磁盘RAID技术。
    通过RAID技术组成的磁盘组,就相当于一个大硬盘,用户可以对它进行分区格式化、建立文件系统操作,跟单个物理硬盘一模一样,唯一不同的是RAID磁盘组的I/O性能比单个硬盘要高很多,同时在数据的安全性也有很大提升。
    根据磁盘组合方式的不同,RAID可以分为RAID0,RAID1、RAID2、RAID3、RAID4、RAID5、RAID6、RAID7、RAID0+1、RAID10等级别,常用的RAID级别有RAID0、RAID1、RAID5、RAID0+1,这里进行简单的介绍。
    RAID0:通过把多块硬盘粘合成一个容量更大的硬盘组,提高磁盘的性能和吞吐量。这种方式成本低,要求至少两个硬盘,但是没用容错和数据修复功能,因此只能用在对数据安全性要求不高的环境中。
    RAID1:也就是磁盘镜像,通过把一个磁盘的数据镜像到另一个磁盘上,最大限度地保证磁盘数据的可靠性和可修复性,具有很高的数据冗余能力,但磁盘的利用率只有50%,因而,成本最高,多用在保存重要数据的场合。
    RAID5:才用了磁盘分段加奇偶校验技术,从而提高了系统可靠性,读出效率很高,写入效率一般,至少需要3块盘。允许一块磁盘故障,而不影响数据的可用性。
    RAID0+1:把RAID0和RAID1技术结合起来就是RAID0+1,至少需要4块盘。此种方式的数据除分布在多个盘上外,每个盘都有其镜像盘,提供全冗能力,同时允许一个磁盘故障,而不影响数据可用性,并具有快速读/写能力。
    通过了解各个RAID级别的性能,可以根据应用的不同特性,选择适合自身的RAID级别,从而保证应用程序在磁盘方面达到最优性能。
    2.4网络带宽
    linux下的各种应用,一般都是基于网络的,因此网络带宽也是影响性能的一个重要因素,低速的、不稳定的网络将导致网络应用程序的访问阻塞,而稳定、高速的网络带宽,可以保证应用程序在网络上畅通无阻地运行。幸运的是现在的网络一般都是千兆带宽或光线网络,带宽问题对于应用程序性能造成的影响也在逐步降低。
    3.操作系统相关资源
    基于操作系统的性能优化也是多方面的,可以从系统安装、系统内核参数、网络参数、文件系统等几个方面进行衡量,下面依次进行简单介绍。
    3.1系统安装优化
    系统优化可以从安装操作系统开始,当安装linux系统时,磁盘的划分,SWAP内存的分配都直接影响以后系统的运行性能。例如,磁盘分配可以遵循应用的需求:对于写操作频繁而对数据安全性要求不高的应用,可以把磁盘做成RAID0;而对于对数据安全性较高,对读写没有特别要求的应用,可以把磁盘做成RAID1;对于对读操作要求较高,而对写操作无特殊要求,并保证数据数据安全性的应用,可以选择RAID5;对于读写要求都很高,并且对数据安全性要求也很高的应用,可以选择RAID10/RAID0+1。这样通过不同的应用需求设置不同的RAID级别,在磁盘底层对系统进行优化操作。
    随着内存价格的降低和内存容量的日益增大,对虚拟内存SWAP的设定,现在已经没有了所谓虚拟内存是物理内存两倍的需求,但是SWAP的设定还是不能忽略,根据经验,如果内存较小(物理内存小于4GB),一般设置SWAP交换分区大小为内存的2倍;如果内存大于8GB小于16GB们可以设置SWAP大小等于或略小于物理内存即可;如果内存大小在16GB以上,原则上可以设置SWAP为0,但并不建议这么做,因为设置一定大小的SWAP还是有一定作用的
    在我们大数据环境中,一般搭建大数据集群式都会禁用swap分区
    3.2内核参数优化
    系统安装完成后,优化工作并没有结束,记下来还可以对系统内核参数进行优化,不过内核参数的优化要和系统中部署的应用结合起来整体考虑。例如,如果系统部署的是Oracle数据库应用,那么就需要对系统共享内存段(kernel.shmmax、kernel.shmmni、kernel.shmall)、系统信号量(kernel.sem)、文件句柄(fs.file-max)等参数进行优化蛇者;如果部署的是web应用,那么就需要根据web应用特性进行网络参数的优化,例如修改net.ipv4.ip_local_port_range、net.ipv4.tcp_tw_reuse、net.core.somaxconn等网络内核参数。
    3.3文件系统优化
    文件系统的优化也是系统资源优化的一个重点,在linux下可选的文件系统有ext2、ext3、ReiserFS、ext4、xfs,根据不同的应用,选择不同的文件系统。
    linux标准文件系统是从VFS开始的,然后是ext,接着是ext2,应该说ext2是Linux上标准的文件系统,ext3是在ext2的基础上增加日志形成的,从VFS到ext4,其设计思路没有太大的变化,都是早期UNIX家族基于超级块和inode的设计理念。
    xfs文件系统是一个高级日志文件系统,xfs通过分布处理磁盘请求、定位数据、保持cache的一致性来提供对文件系统数据的低延迟、高带宽的访问,因此,xfs极具伸缩性,非常健壮,具有优秀的日志记录功能、可扩展性强、快速写入等优点。
    目前服务前端ext4和xfs是主流文件系统,如何选择合适的文件系统,需要根据文件系统的特点加上业务需求综合来定。

    2. 优化方法
    2.1 CPU 使用率
    CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU 上运行任务的不同,又被分为用户 CPU、系统 CPU、等待 I/O CPU、软中断和硬中断等。
    · 用户 CPU 使用率,包括用户态 CPU 使用率(user) 和低优先级用户态 CPU 使用率 (nice),表示 CPU 在用户态运行的时间百分比。用户 CPU 使用率高,通常说明有应用程序比较繁忙。
    · 系统 CPU 使用率,表示 CPU 在内核态运行的时间百分比(不包括中断)。系统 CPU 使用率高,说明内核比较繁忙。
    · 等待 I/O 的 CPU 使用率,通常也称为 iowait,表示等待 I/O 的时间百分比。iowait 高,通常说明系统与硬件设备的 I/O 交互时间比较长。
    · 软中断和硬中断的 CPU 使用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,通常说明系统发生了大量的中断。
    2.2 平均负载(Load Average)
    平均负载,也就是系统的平均活跃进程数,它反映了系统的整体负载情况,主要包括三个数值,分别指过去 1 分钟、过去 5 分钟和过去 15 分钟的平均复制子。
    理想情况下,平均负载等于逻辑 CPU 个数,这表示每个 CPU 都恰好被充分利用。如果平均负载大于逻辑 CPU 个数,就表示负载比较重了。
    2.3 进程上下文切换
    · 无法获取资源而导致的自愿上下文切换。
    · 被系统强制调度导致的非自愿上下文切换。

    2.4 CPU 缓存的命中率
    由于 CPU 发展的速度远快于内存的发展, CPU 的处理速度就比内存的访问速度快得多。这样,CPU 在访问内存的时候,免不了要等待内存的响应。为了协调这两者巨大的性能差距,CPU 缓存(通常是多级缓存)就出现了。
    根据不断增长的热点数据,这些缓存按照大小不同分为 L1、L2、L3 等三级缓存,其中
    L1 和 L2 常用在单核中,L3 则用在多核中。
    从 L1 到 L3,三级缓存的大小依次增大,相应的,性能依次降低(当然比内存还是好
    得多)。而它们的命中率,衡量的是 CPU 缓存的复用情况,命中率越高,则表示性能越好。

    2.5 tcmalloc 替换 ptmalloc
    2.5.1 ptmalloc
    Ptmalloc 采用主-从分配区的模式,当一个线程需要分配资源的时候,从链表中找到一个没加锁的分配区,在进行内存分配。

    小内存分配
    ptmalloc 内部,内存块采用 chunk 管理,并且将大小相似的 chunk 用链表管理,一个链表被称为一个 bin。前 64 个 bin 里,相邻的 bin 内的 chunk 大小相差 8 字节,称为 small bin,后面的是 large bin,large bin 里的 chunk 按先大小,再最近使用的顺序排列,每次分配都找一个最小的能够使用的 chunk。

    image.png

    Chunk 的结构如上所示,A 位表示是不是在主分配区,M 表示是不是 mmap 出来的,P 表示上一个内存紧邻的 chunk 是否在使用,如果没在使用,则 size of previous
    chunk 是上一个 chunk 的大小,否则无意义(而且被用作被分配出去的内存了),正式根据
    P 标记位和 size of previous chunk 在 free 内存块的时候来进行 chunk 合并的。当然,如果 chunk 空闲,mem 里还记录了一些指针用于索引临近大小的 chunk 的,实现原理就不复述了,知道大致作用就行。
    在 free 的时候,ptmalloc 会检查附近的 chunk,并尝试把连续空闲的 chunk 合并成一个大的 chunk,放到 unstored bin 里。但是当很小的 chunk 释放的时候,ptmalloc 会把它并入 fast bin 中。同样,某些时候,fast bin 里的连续内存块会被合并并加入到一个 unsorted bin 里,然后再才进入普通 bin 里。所以 malloc 小内存的时候,是先查找 fast
    bin,再查找 unsorted bin,最后查找普通的 bin,如果 unsorted bin 里的 chunk 不合适,则会把它扔到 bin 里。

    大内存分配
    Ptmalloc 的分配的内存顶部还有一个 top chunk,如果前面的 bin 里的空闲 chunk 都不足以满足需要,就是尝试从 top chunk 里分配内存。如果 top chunk 里也不够,就要从操作系统里拿了。
    还有就是特别大的内存,会直接从系统 mmap 出来,不受 chunk 管理,这样的内存在回收的时候也会 munmap 还给操作系统。

    简而言之,就是:
    小内存: [获取分配区(arena)并加锁] -> fast bin -> unsorted bin -> small bin -> large bin
    -> top chunk -> 扩展堆
    大内存: 直接 mmap

    总结
    释放的时候,几乎是和分配反过来,再加上可一些 chunk 合并和从一个 bin 转移到另一个 bin 的操作。并且如果顶部有足够大的空闲 chunk,则收缩堆顶并还给操作系统。
    介于此,对于 ptmalloc 的内存分配使用有几个注意事项:
    1. Ptmalloc 默认后分配内存先释放,因为内存回收是从 top chunk 开始的。
    2. 避免多线程频繁分配和释放内存,会造成频繁加解锁。
    3. 不要分配长生命周期的内存块,容易造成内碎片,影响内存回收。

    2.5.2 Tcmalloc
    具体实现原理不加以赘述,可自行百度学习之,总结以下特点。
    · Tcmalloc 占用更少的额外空间。例如,分配 N 个 8 字节对象可能要使用大约 8N * 1.01
    字节的空间。即,多用百分之一的空间。Ptmalloc2 使用最少 8 字节描述一个 chunk。
    · 更快。小对象几乎无锁, >32KB 的对象从 CentralCache 中分配使用自旋锁。 并且>32KB 对象都是页面对齐分配,多线程的时候应尽量避免频繁分配,否则也会造成自旋锁的竞争和页面对齐造成的浪费。
    2.6 思维导图

    image.png

    3. 分析工具
    从 CPU 的性能指标出发。当你要查看某个性能指标时,要清楚知道哪些工具可以做到。

    4. 思路
    性能优化并不是没有副作用的,通常情况下 Linux 系统是不需要特意调整某些指标。往往性能优化会带来整体系统的复杂度的上升,降低了可移植性,也可能在调整某个指标的时候导致其他指标异常。
    并不是所有的性能问题都需要去优化,需要对瓶颈点进行优化。比如当前系统有瓶颈,用户 CPU 使用率升高了 10%,而系统系统 CPU 使用率却升高了 50%,这个时候就应该首先优化系统 CPU 使用率。
    4.1 应用程序优化
    从应用程序的角度来说,降低 CPU 使用率最好的方法是,排除所有不必要的工作,只保留最核心的逻辑。比如减少循环层次、减少递归、减少动态内存分配等等。
    常见的几种应用程序的性能优化方法:
    · 编译器优化:很多编译器都会提供优化选项,适当开启它们,在编译阶段你就可以获得编译器的帮助,来提升性能。目前设备采用的优化选项为 Os,相当于 O2.5
    · 算法优化:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序的并发处理能力。
    · 多线程代替多进程:线程的上下文切换并不切换进程地址空间,因此可以降低上下文切换的成本。目前设备采用的是多线程模式。
    · 使用 buffer:经常访问的数据,可以放在内存中缓存起来,这样在下次用时可以直接从内存中获取,加快程序的处理速度。
    · 小内存使用:小内存的申请,在保证栈空间不溢出的情况下,尽量采用栈上申请,少使用动态内存申请,提高程序运行效率。

    4.2 系统优化
    从系统的角度来说,优化 CPU 的运行,一方面要充分利用 CPU 缓存的本地性,加速缓存访问;另一方面,就是要控制进程的 CPU 使用情况,减少进程间的相互影响。
    常见的方法:
    · CPU 绑定:把进程绑定到一个或者多个 CPU 上,可以提高 CPU 缓存的命中率,减少跨 CPU 调度带来的上下文切换问题。
    · 优先级调整:使用 nice 调整进程的优先级,正值调低优先级,负值调高优先级。
    · 中断负载均衡:无论是软中断还是硬中断,它们的中断处理程序都可能消耗大量的 CPU。
    配置 smp_affinity,就可以把中断处理过程自动负载均衡到其他 CPU 上
    · 替换 ptmalloc:当前使用的 gblic 库,其动态内存管理采用就是 ptmalloc。当前应用程序大量使用小尺寸动态内存,可以采用 tcmalloc,在小内存上更快,几乎无锁