最近一周在on-call,这里整理一下Linkedin Learn: Linux performance的内容. 这里主要关注4个方面: CPU, Memory, Disk and FileSystem IO, Network. 其实很多时候,alerts or incidents are derived from performance issues, of course we have insight checks for services themselves. 建议阅读 <<How linux works>>
这本书,里面都讲到了。
此外,极客时间的课程<<Linux性能优化实战>>
非常好,受益匪浅。我还阅读了书籍<<Systems Performance>>
.
From 极客时间
性能领域大师布伦丹·格雷格 personal website
For more comprehensive cheat sheet, please switch to <<On-Call System Performance>>
.
CPU
Understand the meaning of uptime
command(check man). Load average is not CPU usage. 平均负载是指单位时间内,系统处于可运行状态®和不可中断状态(D)的平均进程数,也就是平均活跃进程数(可以这么理解,但源码它实际上是活跃进程数的指数衰减平均值),它和 CPU 使用率并没有直接关系. (有时需要对比系统的运行时间,有的故障可能是系统重启导致的)
1 | # check CPU number |
关于不可中断状态,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题. 不可中断状态实际上是系统对进程和硬件设备的一种保护机制.
综合top
, ps
中查看进程 status code, such as SLsl
,很有意义,可以了解进程的组成等,比如多线程.
1 | man ps |
比如当平均负载为2时, 意味着什么呢? When number of CPU is larger, equal or less then 2. Check lscpu
or grep 'model name' /proc/cpuinfo | wc -l
to see the logical CPU size.
三个不同时间间隔的平均值,其实给我们提供了,分析系统负载趋势的数据来源,让我们能更全面、更立体地理解目前的负载状况. 当平均负载高于 CPU 数量 70% 的时候,你就应该分析排查负载高的问题了。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能. 最推荐的方法,还是把系统的平均负载监控起来(prometheus + grafana),然后根据更多的历史数据,判断负载的变化趋势。当发现负载有明显升高趋势时,比如说负载翻倍了,你再去做分析和调查。
当发现负载高的时候,你可以使用 iostat
, mpstat
, pidstat
等工具,辅助分析负载的来源.
1 | # stress cpu with 1 process |
在查看pidstat 时,可能没有%wait column,则需要升级版本到11.5.5 (centos 8),见sysstat git repo. 或者如果在prod VM上无安装,直接把编译好的文件丢到系统上就可以运行。
理解 CPU 上下文切换: 分为进程上下文切换、线程上下文切换和中断上下文切换。
注意,系统调用
过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。这跟我们通常所说的进程上下文切换是不一样的。进程上下文切换,是指从一个进程切换到另一个进程运行。而系统调用过程中一直是同一个进程在运行。所以,系统调用过程通常称为特权模式切换,而不是上下文切换。但实际上,系统调用过程中,CPU 的上下文切换还是无法避免的。
线程与进程最大的区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位。说白了,所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源。
跟进程上下文不同,中断上下文切换并不涉及到进程的用户态。所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。
vmstat
是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。
sysbench
是一个多线程的基准测试工具,一般用来评估不同系统参数下的数据库负载情况。
1 | # benchmark tool |
如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,或者切换次数出现数量级的增长时,就很可能已经出现了性能问题。
这里比较一下2个测压工具:
stress
基于多进程的,会fork多个进程,导致进程上下文切换,导致cpu %us开销很高;
sysbench
基于多线程的,会创建多个线程,单一进程基于内核线程切换,导致cpu %sy的内核开销很高。
一些思路: 登录到服务器,现在系统负载怎么样。高的话有三种情况,首先是cpu使用率,其次是io使用率,之后就是两者都高。 cpu 使用率高,可能确实是使用率高,也的可能实际处理不高而是进程太多切换上下文频繁,也可能是进程内线程的上下文切换频繁。 io 使用率高,说明 io 请求比较大,可能是文件io,网络io.
这里你一定要记得,当碰到无法解释的 CPU 使用率问题时,先要检查一下是不是短时应用在捣鬼。短时应用的运行时间比较短,很难在 top 或者 ps 这类展示系统概要和进程快照的工具中发现,你需要使用记录事件的工具来配合诊断,比如 execsnoop
或者 perf top
.
当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。从 ps 或者 top 命令的输出中,你可以发现它们都处于 D 状态,也就是不可中断状态. D 状态的进程会导致平均负载升高, I 状态的进程却不会.
正常情况下,不可中断状态在很短时间内就会结束。所以,短时的不可中断状态进程,我们一般可以忽略. 如果系统或硬件发生了故障,进程可能会在不可中断状态D 保持很久,甚至导致系统中出现大量不可中断进程。这时,你就得注意下,系统是不是出现了 I/O 等性能问题。看僵尸进程,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD
信号的处理函数,异步回收资源。如果父进程没有处理子进程的终止,那么子进程就会一直处于僵尸状态。大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建,所以这种情况一定要避免。
1 | # versatile tool for generating system resource statistics |
strace 正是最常用的跟踪进程系统调用的工具, 但僵尸进程都是已经退出的进程,所以就没法儿继续分析它的系统调用:
1 | # -f: trace threads generated |
strace hanging at futex, FUTEX_WAIT: The process is multi-threaded, you are tracing the original parent thread, and it’s doing nothing but waiting for some other threads to finish.
If top, pidstat 这类工具已经不能给出更多的信息了。这时,我们就应该求助那些基于事件记录的动态追踪工具了。
1 | # wait for 15 seconds |
iowait 高不一定代表 I/O 有性能瓶颈。当系统中只有 I/O 类型的进程在运行时,iowait 也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度。碰到 iowait 升高时,需要先用 dstat, pidstat 等工具,确认是不是磁盘 I/O 的问题,然后再找是哪些进程导致了 I/O。
其实除了 iowait,软中断(softirq)
导致 CPU 使用率升高也是最常见的一种性能问题。
为了解决中断处理程序执行过长和中断丢失的问题,Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部: 上半部用来快速处理中断,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作。 下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行。
上半部直接处理硬件请求,也就是我们常说的硬中断,特点是快速执行;而下半部则是由内核触发,也就是我们常说的软中断,特点是延迟执行。
上半部会打断 CPU 正在执行的任务,然后立即执行中断处理程序。而下半部以内核线程的方式执行,并且每个 CPU 都对应一个软中断内核线程,名字为 “ksoftirqd/CPU编号”,比如说, 0 号 CPU 对应的软中断内核线程的名字就是 ksoftirqd/0
。
经常听同事说大量的网络小包会导致性能问题,一直不太理解,从今天的课程来看,大量的小网络包会导致频繁的硬中断和软中断呢.软中断问题在大流量网络中最为常见.
注意cpu 使用率 和 load average 没有直接关系,这个在最开始提到过了,它们各自定义不一样。
1 | # test tool |
在tcpdump的输出中,观察Flag[x]
类型, 比如Flag[S]
表示SYN 包。可以找到source IP 并且通过防火墙隔离。
[ ] cassandra perf analysis find cause?
[x] why cassandra Sleep with high CPU usage? -> multi-threads are running
[x] why process is S
but some threads are R
, parent is doing nothing but waiting children threads.
倪老师您好,我在网上看到一个关于iowait指标的解释,非常形象,但不确定是否准确,帮忙鉴别一下,谢谢, 链接 http://linuxperf.com/?p=33
sar -w 或者 sar -w 1 也能直观的看到每秒生成线程或者进程的数量。Brendan Gregg 确实是这个领域的大师,贡献了很多的技术理念和实践经验。他的《性能之巅》可以和本课对比着看,会有更多的理解.
Memory
Understanding concepts:
这里查了一下tmpfs的解释和作用:
- what is tmpfs
- tmpfs 详解 It is intended to appear as a mounted file system, but data is stored in volatile memory instead of a persistent storage device. A similar construction is a RAM disk, which appears as a virtual disk drive and hosts a disk file system. tmpfs is meant only for ephemeral files
1 | # resize mounted tmpfs size |
tmpfs用途还是较广的,Linux中可以把一些程序的临时文件放置在tmpfs中,利用tmpfs比硬盘速度快的特点来提升系统性能.
需要理解free
, top
, ps
中关于memory 部分的含义, for example: buffers, cached, shared.
/proc
是 Linux 内核提供的一种特殊文件系统,是用户跟内核交互的接口。比方说,用户可以从 /proc 中查询内核的运行状态和配置选项,查询进程的运行状态、统计数据等,当然,你也可以通过 /proc 来修改内核的配置.
1 | # generate files |
在读写普通文件时,会经过文件系统,由文件系统负责与磁盘交互;而读写磁盘或者分区时,就会跳过文件系统,也就是所谓的“裸I/O“。这两种读写方式所使用的缓存是不同的,也就是文中所讲的 Cache 和 Buffer 区别。
我的分析步骤:使用top
和 ps
查询系统中大量占用内存的进程,使用cat /proc/[pid]/status
和pmap -x <pid>
查看某个进程使用内存的情况和动态变化。
查看缓存的实际效果使用缓存命中率
: 是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。
1 | # read file |
Investigate file cache size by pcstat
1 | # install go first |
但同时也要注意,如果我们把 dd 当成测试文件系统性能的工具,由于缓存的存在,就会导致测试结果严重失真。所以测试前要查看文件在缓存中的大小,先清理一下缓存:
1 | echo 3 > /proc/sys/vm/drop_caches |
不过要注意,Buffers 和 Cache 都是操作系统来管理的,应用程序并不能直接控制这些缓存的内容和生命周期。所以,在应用程序开发中,一般要用专门的缓存组件,来进一步提升性能。比如,程序内部可以使用堆或者栈明确声明内存空间,来存储需要缓存的数据。再或者,使用 Redis 这类外部缓存服务,优化数据的访问效率。
dd
命令也支持直接IO的 有选项oflag和iflag 所以dd也可以用来绕过cache buff做测试, 注意直接IO和裸IO 不一样,直接IO是跳过Buffer,裸IO是跳过文件系统(还是有buffer的)。
还要注意的是,就算是第一次读,也会有缓存命中,因为系统会预读一部分到内存中,直接IO虽然跳过了buffer, 但cache hit rate is 100%,根据缓存命中次数(每次命中就是one page 4K size),计算出在当时时间间隔中的命中大小: HITS * 1024 * 4K, 在除以时间间隔,就可知速率K/s.
dd
command bs(block size)
option value, 4K is fine, for large storage hard drive, 1M is good to go, bs
vaule deponds on your RAM size, it is the size that processed of each operation.
Memory leak, 理解内存的分配与回收,哪些虚拟内存段容易发生泄漏问题: heap and 内存映射段(包括动态链接库和共享内存,其中共享内存由程序动态分配和管理)。所以,如果程序在分配后忘了回收,就会导致跟堆内存类似的泄漏问题。
1 | # -r: mem statistic |
memleak的输出结果中addr 表示分配的地址,然后后面会给出哪个stack请求的分配,列出了相关调用。
还有一个常见的内存debug tool: valgrind
.
文件页(file-backed page) 和 匿名页(anonymous page)的区别和回收.
了解swap的机制。watermark
也就是阈值。
事实上不仅 hadoop,包括 ES 在内绝大部分 Java 的应用都建议关 swap,这个和 JVM 的 gc 有关,它在 gc 的时候会遍历所有用到的堆的内存,如果这部分内存是被 swap 出去了,遍历的时候就会有磁盘IO。
Swapping, memory limits, and cgroups. 开启swapping 会使cgroup memory limit失效。
用smem --sort swap
命令可以直接将进程按照swap使用量排序显示.
记录被OOM杀掉的进程:
1 | dmesg | grep -E "kill|oom|out of memory" |
[ ] cgroup mem vs proc mem data? https://www.cnblogs.com/muahao/p/9593869.html
I/O
在 Linux 中一切皆文件。不仅普通的文件和目录,就连块设备、套接字、管道等,也都要通过统一的文件系统来管理。 为了方便管理,Linux 文件系统为每个文件都分配两个数据结构,索引节点(index node)和目录项(directory entry).
索引节点,简称为 inode,用来记录文件的元数据,比如 inode 编号、文件大小、访问权限、修改日期、数据的位置等。索引节点和文件一一对应,它跟文件内容一样,都会被持久化存储到磁盘中。所以记住,索引节点同样占用磁盘空间(df -i <dir>
)。
目录项,简称为 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。不过,不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。
实际上,磁盘读写的最小单位是扇区,然而扇区只有 512B 大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB,也就是由连续的 8 个扇区组成。
目录项、索引节点、逻辑块以及超级块,构成了 Linux 文件系统的四大基本要素。不过,为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层,也就是虚拟文件系统 VFS(Virtual File System)
。
VFS 定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,只需要跟 VFS 提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节。
文件读写方式的各种差异,导致 I/O 的分类多种多样。最常见的有,缓冲与非缓冲 I/O、直接与非直接 I/O、阻塞与非阻塞 I/O、同步与异步 I/O 等。
1 | # check dentry and inode cache |
衡量磁盘IO性能, 须要提到五个常见指标,也就是我们经常用到的,使用率、饱和度、IOPS、吞吐量以及响应时间. 在数据库、大量小文件等这类随机读写比较多的场景中,IOPS 更能反映系统的整体性能;而在多媒体等顺序读写较多的场景中,吞吐量才更能反映系统的整体性能。
1 | # -d: Display the device utilization report |
你可以用 iostat 获得磁盘的 I/O 情况,也可以用 pidstat、iotop 等观察进程的 I/O 情况。 使用率是从时间角度衡量I/O,但是磁盘还可以支持并行写,所以即使使用率100%,有可能还可以接收新的I/O(不饱和)
很有意思的思路: 一般来说,生产系统的应用程序,应该有动态调整日志级别的功能。继续查看源码,你会发现,这个程序也可以调整日志级别。如果你给它发送 SIGUSR1 信号,就可以把日志调整为 INFO 级;发送 SIGUSR2 信号,则会调整为 WARNING 级
在排查应用程序问题时,我们可能需要,在线上环境临时开启应用程序的调试日志。有时候,事后一不小心就忘了调回去。没把线上的日志调高到警告级别,可能会导致 CPU 使用率、磁盘 I/O 等一系列的性能问题,严重时,甚至会影响到同一台服务器上运行的其他应用程序。
1 | # File reads and writes by filename and process. Top for files |
MySQL 的 MyISAM 引擎,主要依赖系统缓存加速磁盘 I/O 的访问。可如果系统中还有其他应用同时运行, MyISAM 引擎很难充分利用系统缓存。缓存可能会被其他应用程序占用,甚至被清理掉。所以,不建议把应用程序的性能优化完全建立在系统缓存上。最好能在应用程序的内部分配内存,构建完全自主控制的缓存;或者使用第三方的缓存应用,比如 Memcached、Redis 等。
学习了redis的一些特性和配置。
为了更客观合理地评估优化效果,我们首先应该对磁盘和文件系统进行基准测试,得到文件系统或者磁盘 I/O 的极限性能。fio 文件系统和磁盘 I/O 性能基准测试工具, fio is a tool that will spawn a number of threads or processes doing a particular type of I/O action as specified by the user. The typical use of fio is to write a job file matching the I/O load one wants to simulate:
Note that 用磁盘路径测试写,会破坏这个磁盘中的文件系统,所以在使用前,你一定要事先做好数据备份。
1 | # 随机读 |
fio 支持 I/O 的重放。借助前面提到过的 blktrace,再配合上 fio,就可以实现对应用程序 I/O 模式的基准测试。你需要先用 blktrace ,记录磁盘设备的 I/O 访问情况;然后使用 fio ,重放 blktrace 的记录。我们就通过 blktrace+fio 的组合使用,得到了应用程序 I/O 模式的基准测试报告.
还谈到了磁盘优化的几个方面,比较深入了,不太理解. 平常没机会从系统层面优化磁盘性能参数。 能做的就是减少磁盘写入,以及错峰操作磁盘。 比如在凌晨或业务低谷时,压缩备份日志,减少对正常业务的影响。
[ ] If threads share the same PID, how can they be identified? [ ] Makefile launches docker container [ ] nsenter 这个和docker exec 有关系,和我之前提到的理解错误的docker login 问题有联系. 可以看一下readme. 认真研究一下这个工具.
Network
本质上是一种进程间通信方式,特别是跨系统的进程间通信,必须要通过网络才能进行。随着高并发、分布式、云计算、微服务等技术的普及,网络的性能也变得越来越重要。
网络接口配置的最大传输单元(MTU),就规定了最大的 IP 包大小。在我们最常用的以太网中,MTU 默认值是 1500(这也是 Linux 的默认值).
1 | # check virtual NI |
实际上,我们通常用带宽、吞吐量、延时、PPS(Packet Per Second)等指标衡量网络的性能。除了这些指标,网络的可用性(网络能否正常通信)、并发连接数(TCP 连接数量)、丢包率(丢包百分比)、重传率(重新传输的网络包比例)等也是常用的性能指标。
而对 TCP 或者 Web 服务来说,更多会用并发连接数和每秒请求数(QPS,Query per Second)等指标,
查看网络配置:
1 | # -s: statistic, show RX and TX |
查看套接字socket:
1 | # -l 表示只显示监听套接字 |
查看throughput and PPS
1 | # check bandwidth |
查看delay:
1 | ping |
Linux 内核自带的高性能网络测试工具 pktgen
, 需要加载内核模块. 用来测试PPS.
TCP/UDP 性能测试: iperf3
.
HTTP 性能: ab
, webbench
, 如果需要mock 负载,使用wrk
.
DNS 不仅方便了人们访问不同的互联网服务,更为很多应用提供了,动态服务发现和全局负载均衡(Global Server Load Balance,GSLB)的机制。这样,DNS 就可以选择离用户最近的 IP 来提供服务。
注意查询IP指定的name server不同,得到的IP也可能不同。
dig
can show the recurring query steps:
1 | # +trace: enable trace |
nslookup debug mode, used when lookup failed:
1 | nslookup -debug <hostname> |
在应用程序的开发过程中,我们必须考虑到 DNS 解析可能带来的性能问题,掌握常见的优化方法. 碰到dns问题最多的就是劫持,现在公网都是强制https,内部用powerdns(open source).
在实际分析网络性能时,先用 tcpdump 抓包,后用 Wireshark 分析,也是一种常用的方法。
1 | # 禁止接收从DNS服务器发送过来并包含googleusercontent的包 |
实际上,根据 IP 地址反查域名、根据端口号反查协议名称,是很多网络工具默认的行为,而这往往会导致性能工具的工作缓慢.
[ ] Linux ring buffer with dmesg [ ] DMA ring [ ] conntrack module [ ] Diagnose NAT issue [ ] 酷壳 [ ] tcpdump man [ ] wireshark doc [ ] level-triggered vs edge-triggered [ ] thundering herd 惊群 [ ] 不懂的很多。。可以从go network programming开始学习? 网络学习吃力的同学,先去把林沛满老师两本Wireshark分析网络看完 [ ] hping3 a network penetration test tool, security auditiing, fw test
[ ] blog 动态追踪技术漫谈
From LinkedIn Learning
time
in shell is a keyword, it is also a command, they have different output.
1 | yum install -y time |
If you want to test performance of 2 similar commands, put each in a loop and time
it as a whole to see the differences, or using strace -c -o /tmp/result.out ./script
and head
the first several lines of the result to see what system calls cost much.
/proc
is mounted at boot time, see mount detail by mount | grep proc
command. proc files provide kernel info, printing the contents of the proc file causes the corresponding function in the kernel to be called to produce fresh value. we can also write to proc file to change kernal parameters. /proc
is a pseudo filesystem, so ls -l
the length may be 0.
/proc
files under /proc/sys
represent kernel variables.
CPU
Packages for performance tools, yum install them and rpm -ql
to see utilities contained:
- sysstat: iostat, mpstat, nfsiostat-sysstat, pidstat, sar
- procps-ng: free, pmap, ps, since, tload, top, uptime, vmstat, watch
- perf:
sudo perf record find / -xdev -name core >/dev/null 2>&1; sudo perf report
主要说了sar, top, cpuinfo 以及scheduling priority and nice value. (具体记录在其他blog中,搜一下关键字)
Throughput
, important for server, fewer context switch and longer time silces. throughput 和 responsiveness 需要权衡,好的throughput 意味着尽可能run 单一的任务,这样context switch就少. Responsiveness
, important for interactive or control system, requires quick context switch and shorter time slices and less page faults.
Can configure the kernel to preemptible or not, preemption means context switch.
Linux kernel has 3 choices for the kind of preemption
it employs.
- None: no preemption
- Voluntary: kernel checks frequently for placement (the most commonly choice)
- Preempt: schedule preempts unless kernel is in a critical section.
Memory
/proc/meminfo
file, the MemAvailable
value is space for program without swapping (includes reclaimable cache and buffer), important.
1 | MemTotal: 32779460 kB |
htop
command is similar to top
but with colorful display. From the options you will sort by different categories, e.g. %CPU, $MEM, etc.
Translation lookaside buffer
(TLB), use for virtual address mapped to physical addresses. Linux supportss having huge pages, can use sysctl
config at runtime or during bootstrap (/etc/sysctl.d).
Page faults
, a process uses an address that is not mapped or even not RAM resident. minor or major page faults, minor is not a big deal, major has disk I/O involved, much slower. Linux is on-demand page system. (how linux works 这本书也提到了)
Disk
atop
command is also helpful.
后面谈到了如何测试不同filesystem 的performance, 可以用dd 生成大文件用作loop device, 格式化为不用的filesystem,然后mount,随后进行大量的文件或文件夹创建,记录时间,在对文件和文件夹进行操作,记录时间.