第五周

因为 io 章节上周就完成了,所以网络这周早早地就完成了任务。

C10k 那一章内容比较多,面试前可以多看看。

我们常用带宽、吞吐量、延时、PPS(Packet Per Second)等指标衡量网络的性能。

带宽表示链路的最大传输速率,单位通常为 b/s (比特 / 秒)。吞吐量,表示单位时间内成功传输的数据量,单位通常为 b/s(比特 / 秒)或者 B/s(字节 / 秒)。吞吐量受带宽限制,而吞吐量 / 带宽,也就是该网络的使用率。

使用套接字接口的时候,也要分配一个文件描述符,后续所有的 I/O 都通过这个文件描述符来操作(包括IO模型中要判断可读写状态)。

第四周

周末花了一个整块的时间来看 I/O 这一章。

对 I/O 请求排序的过程,也就是我们熟悉的 I/O 调度。Linux 内核支持四种 I/O 调度算法,分别是 NONE、NOOP、CFQ 以及 DeadLine。

磁盘性能的衡量标准,有五个指标:使用率,饱和率,IOPS,吞吐量,响应时间。

过高的使用率(比如超过 60%)通常意味着磁盘 I/O 存在性能瓶颈。

阻塞 / 非阻塞和同步 / 异步,其实就是两个不同角度的 I/O 划分方式。它们描述的对象也不同,阻塞 / 非阻塞针对的是 I/O 调用者(即应用程序),而同步 / 异步针对的是 I/O 执行者(即系统)。

IO性能问题的思路:

  1. 首先可以通过 top 查看机器的整体负载情况,一般会出现 CPU 的 iowait 较高的现象;
  2. 然后使用 pidstat -d 1 找到读写磁盘较高的进程;
  3. 然后通过 strace -f -TT 进行跟踪,查看系统读写调用的频率和时间;
  4. 通过 lsof 找到 strace 中的文件描述符对应的文件 opensnoop 可以找到对应的问题位置。

第三周

本周主要是学习关于内存相关的知识,有些关于块设备的操作,怕把环境搞崩溃了,没跟着教程做。不过看着命令集不断地“壮大”,还是挺不错的。

内存调优最重要的就是,保证应用程序的热点数据放到内存中,并尽量减少换页和交换。

活跃和非活跃的内存页,按照类型的不同,又分别分为文件页和匿名页,对应着缓存回收和 Swap 回收。

OOM 触发的时机基于虚拟内存。换句话说,进程在申请内存时,如果申请的虚拟内存加上服务器实际已用的内存之和,比总的物理内存还大,就会触发 OOM。

磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储。在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的“裸 I/O”。文件系统管理的缓存,其实就是 Cache 的一部分。而裸磁盘的缓存,用的正是 Buffer。

第二周

由于是双周,少了一个周日的时间来看专栏,有点赶,加上 0607 晚上又有一个交流会,这次笔记写得有点匆忙。当然,专栏上的文字内容是早就看完了。

这次的感觉是重新看一遍感觉比上次看要容易很多。果然难的东西要看第二遍、第三遍就比较清晰。

持续在思考的就是怎样把这些东西融汇贯通?

如果能在实践中直接用的话,是比较有成就感的。比如:https://qcrao.com/2020/04/27/codec-accident/

听了大家的分享,赶紧有一群人一起学习还是挺好的。

先完成,再完美。

后面的模式是每周写一些心得感受,然后更新汇总的命令。争取在专栏完成的时候有一个速查手册,遇到性能方面的问题能直接拿出来参考。

all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 查看 CPU 核心数
$ grep 'model name' /proc/cpuinfo | wc -l

# 节拍率 HZ
$ grep 'CONFIG_HZ=' /boot/config-$(uname -r)

# 从所有进程中查找PID是24344的进程
$ ps aux | grep 24344

# 用树状形式显示所有进程之间的关系
$ pstree | grep stress

# -a 表示输出命令行选项
# p表PID
# s表示指定进程的父进程
$ pstree -aps 3084

# -t表示显示线程,-a表示显示命令行参数
pstree -t -a -p 27458

# 查看启动用户
ps aux | grep "nginx: worker process" | awk '{print $1}'

# 显示none就是物理机,显示 kvm 则是虚拟机
$ systemd-detect-virt

# 内核版本
$ uname -r

# 查看装了哪些 linux-tools
$ apt list linux-tools*
# 显示更多信息
$ apt show linux-tools* -a

# 可以查看系统的整体内存和 Swap 使用情况。
# 查看整个系统物理内存 Mem 和交换分区 Swap 的使用情况,以字节为单位
# 读取 /proc/meminfo 获得数据
# Buffer 是对磁盘数据的缓存,而 Cache 是对文件数据的缓存,它们既会用在读请求中,也会用在写请求中
$ free

# 显示处理器在 Node 的分布情况
$ numactl --hardware

# 调整文件页和匿名页的回收倾向。
# 系统会根据此值,倾向于选择合适的回收类型,比如回收不活跃的匿名页,或者不活跃的文件页。
$ cat /proc/sys/vm/swappiness

# 关闭 swap 机制
$ swapoff -a

# 常用的 Swap 空间清理方法
$ swapoff -a && swapon -a

# 开启 swap 机制
# 创建Swap文件
$ fallocate -l 8G /mnt/swapfile
# 修改权限只有根用户可以访问
$ chmod 600 /mnt/swapfile
# 配置Swap文件
$ mkswap /mnt/swapfile
# 开启Swap
$ swapon /mnt/swapfile

# 查询 OOM 日志,查看哪些进程被 OOM 杀死了
$ dmesg | grep -i "Out of memory"

# 统计所有进程的物理内存使用量
# 使用grep查找Pss指标后,再用awk计算累加值
# 每个进程的 PSS ,是指把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和。
$ grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kB\n", total }'

# 日志处理工具:awk,sed,grep,sort,uniq,xargs
# 读取日志文件,每行按空格划分,打印出第 1 列,按字母顺序排序,过滤重复行且输出计数,对数字进行排序(-n)并逆序返回(-r),最后输出前 5 行
cat x.log | awk '{print $1}' | sort | uniq -c | sort -r -n | head -n 5

# 查看文件系统的磁盘空间使用情况
df /dev/sda1
# -h 能提供更好的可读性
df -h /dev/sda1
# -i 查看索引节点的使用情况。索引节点的容量(个数)在格式化磁盘时就会设置好
# 当你发现索引节点空间不足,但磁盘空间充足时,很可能就是过多小文件导致的。
df -ih /dev/sda1

# 找到占用内存最多的缓存类型
# 按下c按照缓存大小排序,按下a按照活跃对象数排序
slabtop

# 查看进程打开文件列表,不过,这里的“文件”不只有普通文件,还包括了目录、块设备、动态库、网络套接字
# u 是指以读写方式
lsof -p #pid

# 给进程 18940 发送 SIGUSR2 信号
kill -SIGUSR2 18940

# 循环执行 curl 命令
$ while true; do time curl http://192.168.0.10:10000/popularity/word; sleep 1; done

# 根据线程号查看进程号
ps -efT | grep 514

# 重启 docker
sudo service docker restart

# 上一条命令退出时的返回值。0 成功,1 失败
echo $?

# 查看指定进程号对应的网络连接
# -i表示显示网络套接字信息
nsenter --target 12092 --net -- lsof -i

# 查看网络配置状态
ifconfig eth0
ip -s addr show dev eth0

# 查看网卡速度,也就是带宽,Bandwidth
ethtool eth0 | grep Speed

# -c3表示发送三次ICMP包后停止
$ ping -c3 114.114.114.114

install

1
2
3
4
# 下载安装 execsnoop
1 cd /usr/bin
2 wget https://raw.githubusercontent.com/brendangregg/perf-tools/master/execsnoop
3 chmod 755 execsnoop

bcc

1
2
3
4
5
6
7
8
# 下载安装 bcc
# Install bcc
# bcc 软件包提供了一系列的 Linux 性能分析工具,常用来动态追踪进程和内核的行为。
# 安装完成后,位于 /usr/share/bcc/tools
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
$ echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list
$ sudo apt-get update
$ sudo apt-get install -y bcc-tools libbcc-examples linux-headers-$(uname -r)

先 cd 到安装目录:

cd /usr/share/bcc/tools

filetop

1
2
3
# -C 选项表示输出新内容时不清空屏幕 
# 观察一下文件的读写情况。分析系统调用 write()
./filetop -C

memleak

专门用来检测内存泄漏的工具,memleak。memleak 可以跟踪系统或指定进程的内存分配、释放请求,然后定期输出一个未释放内存和相应调用栈的汇总情况(默认 5 秒)。

类似的工具:valgrind

位于 /usr/share/bcc/tools/memleak

1
2
3
# -a 表示显示每个内存分配请求的大小以及地址
# -p 指定案例应用的PID号
$ /usr/share/bcc/tools/memleak -a -p $(pidof app)

cachestat

提供了整个操作系统缓存的读写命中情况。

cachetop

提供了每个进程的缓存命中情况。cachetop 工具并不把直接 I/O 算进来。

1
2
# 找出 top 5
$ /usr/share/bcc/tools/cachetop 5

opensnoop

1
2
# 可以动态跟踪内核中的 open 系统调用。查看系统调用的文件路径
./opensnoop

ab

ab(apache bench)是一个常用的 HTTP 服务性能测试工具。

1
2
3
4
5
# 并发10个请求测试Nginx性能,总共测试100个请求
$ ab -c 10 -n 100 http://192.168.0.10:10000/

# 并发5个请求测试Nginx性能,总共测试10分钟
$ ab -c 5 -t 600 http://192.168.0.10:10000/

stress

安装:apt install stress

stress 是一个 Linux 系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景。

  • 模拟一个 CPU 使用率 100% 的场景
1
stress --cpu 1 --timeout 600
  • 模拟 I/O 压力,即不停地执行 sync
1
stress -i 1 --timeout 600
  • 模拟 8 个进程
1
stress -c 8 --timeout 600

sysbench

安装:apt install sysbench

sysbench 是一个多线程的基准测试工具,一般用来评估不同系统参数下的数据库负载情况。

1
2
# 以10个线程运行5分钟的基准测试,模拟多线程切换的问题
$ sysbench --threads=10 --max-time=300 threads run

sysstat

安装:apt install sysstat

sysstat 包含了常用的 Linux 性能工具,用来监控和分析系统的性能。案例会用到这个包的两个命令 mpstat 和 pidstat。

mpstat 是一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标。

pidstat 是一个常用的进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标。pidstat 只是一个进程的性能分析工具,并不提供任何关于中断的详细信息。

  • 查看所有 CPU 的情况
1
2
# -P ALL 表示监控所有CPU,后面数字5表示间隔5秒后输出一组数据
$ mpstat -P ALL 5
  • 以进程视角查看 CPU 使用率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 每间隔5秒输出一组数据,-u表示CPU指标(周期性)
$ pidstat -u 5

# 间隔5秒后输出一组数据,-u表示CPU指标(一次)
$ pidstat -u 5 1

# 每隔5秒输出1组数据。查看每个进程上下文切换的情况
$ pidstat -w 5

# 每隔1秒输出1组数据(需要 Ctrl+C 才结束)
# -w参数表示输出进程切换指标,而-u参数则表示输出CPU使用指标
$ pidstat -w -u 1

# 每隔1秒输出一组数据(需要 Ctrl+C 才结束)
# -wt 参数表示输出线程的上下文切换指标
$ pidstat -wt 1

# 指定进程
pidstat -p 24344

# -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒输出 3 组数据
$ pidstat -d -p 4344 1 3

# 间隔 1 秒输出多组数据 (这里是 20 组)
$ pidstat -d 1 20

vmstat

vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。

  • 系统整体情况。如系统整体的上下问切换情况
1
2
3
4
5
6
7
# 每隔5秒输出1组数据。
# buff 和 cache 的单位是 KB
# bi 和 bo 则分别表示块设备读取和写入的大小,单位为块/秒。因为 Linux 中块的大小是 1KB,所以这个单位也就等价于 KB/s
$ vmstat 5

# 间隔1秒后输出1组数据
$ vmstat 1 1

dstat

可以同时查看 CPU 和 I/O 这两种资源的使用情况。

1
2
# 间隔1秒输出10组数据
$ dstat 1 10

/proc

/proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts

# 只保留各个CPU的数据。/proc/stat 提供的就是系统的 CPU 和任务统计信息
# 以进程的视角 /proc/[pid]/stat
$ cat /proc/stat | grep ^cpu

# 将 sshd 进程的 oom_adj 调小为 -16,这样, sshd 进程就不容易被 OOM 杀死
# [-17, 15],其中 -17 表示禁止 OOM
$ echo -16 > /proc/$(pidof sshd)/oom_adj

# 通过 /proc 系统清理系统缓存
# 写入 3 表示清理文件页、目录项、Inodes 等各种缓存
$ echo 3 > /proc/sys/vm/drop_caches
# 清理 dentries 和 inodes
echo 2 > /proc/sys/vm/drop_caches
# 清理 page cache
echo 1 > /proc/sys/vm/drop_caches

# 可查看三个内存阈值(页最小阈值、页低阈值和页高阈值)
$ cat /proc/zoneinfo

# -d 表示高亮变化的字段
# -A 表示仅显示Normal行以及之后的15行输出;-B 表示之前
$ watch -d grep -A 15 'Normal' /proc/zoneinfo

# 按VmSwap使用量对进程排序,输出进程名称、进程ID以及SWAP用量
$ for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | headdockerd

# grep表示只保留包含active的指标
# -i 忽略大小写
# sort表示按照字母顺序排序
$ cat /proc/meminfo | grep -i active | sort


# 查看所有目录项和各种文件系统索引节点的缓存情况
# dentry 行表示目录项缓存,inode_cache 行,表示 VFS 索引节点缓存,其余的则是各种文件系统的索引节点缓存
cat /proc/slabinfo | grep -E '^#|dentry|inode'

重调度中断(RES),这个中断类型表示,唤醒空闲状态的 CPU 来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同 CPU 的机制,通常也被称为处理器间中断(Inter-Processor Interrupts,IPI)。

perf

perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。

1
2
3
4
5
6
7
8
9
10
11
# 它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找热点函数
# 虽然实时展示了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续的分析
$ perf top

# perf record 则提供了保存数据的功能
# 加上 -g 参数,开启调用关系的采样
$ perf record # 按Ctrl+C终止采样
$ perf report # 展示类似于perf top的报告

# -g开启调用关系分析,-p指定php-fpm的进程号21515
$ perf top -g -p 21515

strace

strace 正是最常用的跟踪进程系统调用的工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
# -p 参数指定 PID 号
$ strace -p 6082

# 过滤一下 write 调用
strace -p 12280 2>&1 | grep write

# 跟踪所有线程
strace -fp pid

# -f表示跟踪子进程和子线程,-T表示显示系统调用的时长,-tt表示显示跟踪时间
strace -f -T -tt -p 9085
# -e 指定系统调用
strace -f -p 9085 -T -tt -e fdatasync

sar

1
2
3
4
5
6
7
8
# 间隔1秒输出一组数据
# -r表示显示内存使用情况,-S表示显示Swap使用情况
# 指标名称前面的 kb 前缀,表示这些指标的单位是 KB。
$ sar -r -S 1

# 查看网络的统计信息
# 数字1表示每隔1秒输出一组数据
$ sar -n DEV 1

iostat

1
2
3
4
# -d -x表示显示所有磁盘I/O的指标
# 它提供了每个磁盘的使用率、IOPS、吞吐量等各种常见的性能指标
# 指标实际上来自 /proc/diskstats
iostat -d -x 1

pidstat

1
2
# 加上 -d 参数,你就可以看到进程的 I/O 情况
pidstat -d 1

iotop

1
2
# 按照 I/O 大小对进程排序
iotop

docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看容器状态
docker ps

# 强制删除容器
docker rm -f dockerid/dockername

# 查看数据库底层文件
# MYD 文件用来存储表的数据;
# MYI 文件用来存储表的索引;
# frm 文件用来存储表的元信息(比如表结构);
# opt 文件则用来存储数据库的元信息(比如字符集、字符校验规则等)。
docker exec -it mysql ls /var/lib/mysql/test/

# 查询 mysqld 配置的数据路径
docker exec -i -t mysql mysql -e 'show global variables like "%datadir%";'

# 进入 mysql 命令行
docker exec -i -t mysql mysql

# 获取 redis append 策略配置
docker exec -it redis redis-cli config get 'append*'
# 设置 appendfsync 策略
docker exec -it redis redis-cli config set appendfsync everysec

MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看当前正在执行的 SQL 语句
show full processlist;

# explain
explain select * from products where productName='geektime';

# 查询表结构
show create table products;

# 加索引
CREATE INDEX products_index ON products (productName);
# 指定一个前缀长度
CREATE INDEX products_index ON products (productName(64));
# 删除索引
DROP INDEX products_index ON products;

netstat

1
2
3
4
5
6
7
8
# head -n 3 表示只显示前面3行
# -l 表示只显示监听套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ netstat -nlp | head -n 3

# 查看协议栈的信息
netstat -s

ss

1
2
3
4
5
6
7
8
# -l 表示只显示监听套接字
# -t 表示只显示 TCP 套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ ss -ltnp | head -n 3

# 查看协议栈的信息
ss -s

wrk

1
2
3
# -c表示并发连接数1000,-t表示线程数为2
# wrk 支持 lua 脚本
$ wrk -c 1000 -t 2 http://192.168.0.30/