DevOps · #performance#linux#tuning#kernel

Linux性能调优:从内核参数到应用优化

2024.08.07 7 min 2.9k
// 目录 · contents

前言

Linux性能调优是一门既需要理论基础又需要实践经验的技术。一个配置不当的系统可能浪费30%-50%的硬件能力。本文将从CPU、内存、磁盘I/O和网络四个维度,系统讲解Linux性能调优方法。

性能分析方法论

USE方法

USE方法(Utilization, Saturation, Errors)适用于分析系统资源:

graph TB
    subgraph USE["USE方法"]
        U["Utilization 利用率<br>资源繁忙时间占比"]
        S["Saturation 饱和度<br>排队等待的工作量"]
        E["Errors 错误<br>错误事件计数"]
    end

    subgraph Resources["系统资源"]
        CPU["CPU"]
        MEM["Memory"]
        DISK["Disk I/O"]
        NET["Network"]
    end

    USE --> Resources
资源 利用率 饱和度 错误
CPU mpstat -P ALL 1 vmstat的r列 perf stat
Memory free -m vmstat的si/so列 dmesg \| grep oom
Disk iostat -xz 1 iostat的avgqu-sz smartctl
Network sar -n DEV 1 ss -s的overflowed ip -s link

性能分析工具全景图

graph TB
    subgraph Observability["观测工具"]
        subgraph Basic["基础工具"]
            top["top/htop"]
            vmstat["vmstat"]
            iostat["iostat"]
            sar["sar"]
            free["free"]
            ss["ss/netstat"]
        end

        subgraph Advanced["高级工具"]
            perf["perf"]
            bpftrace["bpftrace"]
            strace["strace"]
            ftrace["ftrace"]
            bcc["BCC tools"]
        end

        subgraph Profiling["剖析工具"]
            flamegraph["Flame Graph"]
            perfrecord["perf record"]
            offcpu["Off-CPU Analysis"]
        end
    end

CPU调度优化

查看CPU状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# CPU概览
lscpu
nproc

# 实时CPU使用率
mpstat -P ALL 1
# %usr - 用户态CPU(应用程序)
# %sys - 内核态CPU(系统调用)
# %iowait - I/O等待
# %idle - 空闲

# 进程级CPU分析
pidstat 1
pidstat -p <pid> -t 1 # 线程级别

# CPU上下文切换
vmstat 1
# cs列 = 每秒上下文切换次数
# r列 = 运行队列长度(应 < CPU核数*2)

CPU亲和性(CPU Affinity)

将进程绑定到特定CPU核心,减少缓存失效和上下文切换:

1
2
3
4
5
6
7
8
9
10
11
12
# 查看进程的CPU亲和性
taskset -p <pid>

# 绑定进程到CPU 0-3
taskset -c 0-3 ./myapp

# 修改运行中进程的亲和性
taskset -p -c 0,1 <pid>

# 使用cgroup v2绑定CPU
echo "0-3" > /sys/fs/cgroup/myapp/cpuset.cpus
echo "0" > /sys/fs/cgroup/myapp/cpuset.mems

调度器参数

1
2
3
4
5
6
7
8
9
10
11
12
# CFS调度器参数
# 调度周期(默认6ms,减小可降低延迟但增加开销)
sysctl kernel.sched_min_granularity_ns=3000000

# 唤醒抢占粒度
sysctl kernel.sched_wakeup_granularity_ns=4000000

# 进程迁移成本(增大可减少跨CPU迁移)
sysctl kernel.sched_migration_cost_ns=5000000

# NUMA均衡
sysctl kernel.numa_balancing=1
graph LR
    subgraph CFS["CFS调度器"]
        VRT["虚拟运行时间<br>(vruntime)"]
        RBTree["红黑树<br>按vruntime排序"]
        Pick["选择vruntime<br>最小的进程"]
    end

    VRT --> RBTree --> Pick --> |"运行"| Process["进程"]
    Process --> |"更新vruntime"| VRT

内存管理优化

内存状态分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 内存概览
free -h
cat /proc/meminfo

# 关键指标
# MemTotal: 总物理内存
# MemAvailable: 可用内存(包含可回收缓存)
# Buffers: 磁盘缓冲
# Cached: 页面缓存
# SwapTotal: 总Swap空间
# SwapFree: 空闲Swap

# 进程内存分析
pmap -x <pid>
smem -t -k

vm.swappiness

控制内核使用Swap的倾向(0-200,默认60):

1
2
3
4
5
6
7
8
# 查看当前值
cat /proc/sys/vm/swappiness

# 数据库服务器(减少Swap使用)
sysctl -w vm.swappiness=10

# 持久化配置
echo "vm.swappiness=10" >> /etc/sysctl.d/99-tuning.conf
graph LR
    subgraph Swappiness["vm.swappiness"]
        Low["swappiness=0~10<br>尽量不使用Swap<br>适合:数据库"]
        Mid["swappiness=30~60<br>适度平衡<br>适合:通用服务器"]
        High["swappiness=100+<br>积极使用Swap<br>适合:桌面系统"]
    end

Huge Pages(大页内存)

大页减少TLB(Translation Lookaside Buffer)Miss,适用于大内存应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看当前大页配置
cat /proc/meminfo | grep Huge
# HugePages_Total: 0
# HugePages_Free: 0
# Hugepagesize: 2048 kB

# 配置2MB大页(需要连续物理内存)
sysctl -w vm.nr_hugepages=1024 # 分配1024个2MB大页 = 2GB

# 透明大页(THP)
# 对大多数应用有益,但数据库场景可能导致延迟抖动
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never

# 数据库服务器建议关闭THP
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo madvise > /sys/kernel/mm/transparent_hugepage/defrag

内存回收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 脏页回写阈值(占总内存百分比)
sysctl -w vm.dirty_ratio=15 # 同步回写阈值(默认20)
sysctl -w vm.dirty_background_ratio=5 # 后台回写阈值(默认10)

# 或使用绝对字节数
sysctl -w vm.dirty_bytes=1073741824 # 1GB
sysctl -w vm.dirty_background_bytes=536870912 # 512MB

# 脏页过期时间(厘秒,默认3000=30秒)
sysctl -w vm.dirty_expire_centisecs=1500

# OOM相关
sysctl -w vm.overcommit_memory=0 # 0=启发式, 1=总是允许, 2=严格限制
sysctl -w vm.panic_on_oom=0 # OOM时不panic

磁盘I/O优化

I/O状态分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# I/O统计
iostat -xz 1
# rrqm/s, wrqm/s - 合并读写请求数
# r/s, w/s - 每秒读写次数
# rMB/s, wMB/s - 每秒读写MB数
# avgrq-sz - 平均请求大小
# avgqu-sz - 平均队列长度(饱和度指标)
# await - 平均I/O等待时间(ms)
# r_await, w_await - 读写等待时间
# %util - 设备利用率

# 进程级I/O
iotop -oP
pidstat -d 1

I/O调度器

1
2
3
4
5
6
7
8
9
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# [mq-deadline] none

# 修改调度器
echo "mq-deadline" > /sys/block/sda/queue/scheduler

# NVMe设备通常使用none(硬件直接处理)
echo "none" > /sys/block/nvme0n1/queue/scheduler
调度器 特点 适用场景
none 不排序,FIFO NVMe SSD
mq-deadline 保证截止时间 通用SSD/HDD
bfq 公平带宽分配 桌面交互场景
kyber 低延迟优化 高速SSD

预读(Readahead)

1
2
3
4
5
6
7
8
9
10
# 查看当前预读大小(512字节扇区数)
cat /sys/block/sda/queue/read_ahead_kb
# 默认128KB

# 顺序读优化(增大预读)
echo 2048 > /sys/block/sda/queue/read_ahead_kb # 2MB

# 使用blockdev命令
blockdev --getra /dev/sda
blockdev --setra 4096 /dev/sda # 设置为2MB (4096*512=2MB)

文件系统优化

1
2
3
4
5
6
7
8
9
10
11
# ext4挂载选项优化
mount -o noatime,nodiratime,discard /dev/sda1 /data
# noatime - 不更新访问时间(减少写I/O)
# nodiratime - 不更新目录访问时间
# discard - 启用TRIM(SSD)

# /etc/fstab
/dev/sda1 /data ext4 defaults,noatime,nodiratime,discard 0 2

# XFS挂载优化
mount -o noatime,inode64,logbufs=8 /dev/sda1 /data

网络调优

TCP缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
# TCP接收/发送缓冲区(min, default, max)
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"

# 全局socket缓冲区
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.core.rmem_default=262144
sysctl -w net.core.wmem_default=262144

# TCP窗口缩放(高带宽延迟网络必须)
sysctl -w net.ipv4.tcp_window_scaling=1

连接队列

graph LR
    Client["客户端"] --> |"SYN"| SYN_Q["SYN队列<br>(半连接队列)<br>tcp_max_syn_backlog"]
    SYN_Q --> |"SYN+ACK"| Client
    Client --> |"ACK"| Accept_Q["Accept队列<br>(全连接队列)<br>somaxconn"]
    Accept_Q --> |"accept()"| App["应用程序"]
1
2
3
4
5
6
7
8
9
10
11
12
# SYN队列大小(半连接)
sysctl -w net.ipv4.tcp_max_syn_backlog=65535

# Accept队列大小(全连接)
sysctl -w net.core.somaxconn=65535

# 队列溢出时的行为
sysctl -w net.ipv4.tcp_abort_on_overflow=0 # 0=丢弃, 1=发送RST

# SYN Flood防护
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=65535

TIME_WAIT优化

1
2
3
4
5
6
7
8
# TIME_WAIT套接字复用
sysctl -w net.ipv4.tcp_tw_reuse=1

# 最大TIME_WAIT数量
sysctl -w net.ipv4.tcp_max_tw_buckets=262144

# FIN超时时间
sysctl -w net.ipv4.tcp_fin_timeout=15

端口范围与连接跟踪

1
2
3
4
5
6
7
8
9
10
# 本地端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 连接跟踪表大小(防火墙/NAT场景)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

# 文件描述符限制
sysctl -w fs.file-max=2097152
ulimit -n 1048576

完整的网络调优sysctl配置

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
# /etc/sysctl.d/99-network-tuning.conf

# TCP缓冲区
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535

# TCP优化
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

# 安全
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1

# 端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 文件描述符
fs.file-max = 2097152

perf工具实战

CPU性能分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 采集CPU性能事件
perf stat -d ./myapp
# 输出:
# 1,234,567,890 cycles
# 567,890,123 instructions # 0.46 insn per cycle
# 123,456,789 cache-misses # 12.5% of cache references
# 45,678 page-faults
# 123 context-switches

# CPU采样(生成火焰图数据)
perf record -g -p <pid> -- sleep 30

# 查看报告
perf report

# 生成火焰图
perf script | stackcollapse-perf.pl | flamegraph.pl > flamegraph.svg

火焰图分析

graph TB
    subgraph FlameGraph["火焰图解读"]
        direction TB
        Root["全部采样"]
        Func1["main() - 100%"]
        Func2["handleRequest() - 60%"]
        Func3["queryDB() - 35%"]
        Func4["parseJSON() - 25%"]
        Func5["executeSQL() - 30%"]
        Func6["serialize() - 5%"]

        Root --> Func1
        Func1 --> Func2
        Func1 --> Func4
        Func2 --> Func3
        Func3 --> Func5
        Func3 --> Func6
    end
1
2
3
4
5
6
7
8
9
10
11
# On-CPU火焰图
perf record -F 99 -g -p <pid> -- sleep 30
perf script > perf.out
./stackcollapse-perf.pl perf.out | ./flamegraph.pl > oncpu.svg

# Off-CPU分析(使用bpftrace)
bpftrace -e '
kprobe:finish_task_switch {
@[kstack, comm] = count();
}
' > offcpu.out

常用perf命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看热点函数
perf top -p <pid>

# 跟踪系统调用延迟
perf trace -p <pid> -s

# 分析缓存命中率
perf stat -e cache-references,cache-misses -p <pid> -- sleep 10

# 分析分支预测
perf stat -e branch-instructions,branch-misses -p <pid> -- sleep 10

# 内存访问分析
perf mem record -p <pid> -- sleep 10
perf mem report

综合调优示例

高并发Web服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# /etc/sysctl.d/99-webserver.conf

# 网络
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535

# TCP缓冲区
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 内存
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5

# 文件
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
1
2
3
4
5
# /etc/security/limits.d/99-webserver.conf
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535

数据库服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# /etc/sysctl.d/99-database.conf

# 内存优化(数据库最关键)
vm.swappiness = 1
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
vm.dirty_expire_centisecs = 500

# 禁用透明大页
# echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 网络
net.core.somaxconn = 4096
net.ipv4.tcp_keepalive_time = 600

# 共享内存(PostgreSQL需要)
kernel.shmmax = 68719476736 # 64GB
kernel.shmall = 16777216

总结

Linux性能调优的核心方法:

  1. 先测量后优化:使用USE方法识别瓶颈,避免盲目调参
  2. 一次只改一个参数:确认效果后再调整下一个
  3. CPU:关注上下文切换、调度延迟,合理使用CPU亲和性
  4. 内存:调整swappiness、大页配置,监控OOM事件
  5. 磁盘I/O:选择合适的调度器,优化预读和文件系统挂载选项
  6. 网络:调整TCP缓冲区、连接队列、TIME_WAIT参数
  7. 持久化配置:所有优化写入/etc/sysctl.d/,重启后生效

性能调优是一个持续的过程,需要结合监控数据不断迭代。切忌照搬网上的”最优配置”,每个系统的工作负载不同,最优参数也不同。

作者 · authorzt
发布 · date2024-08-07
篇幅 · length2.9k 字 · 7 min
许可 · licenseCC BY-SA 4.0
$ echo "comments" · 评论