强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 9 章:性能调优

第 9 章:性能调优

让每一块存储设备都发挥极致性能


9.1 性能调优概述

9.1.1 性能影响因素

Bcachefs 性能影响因素:

┌─────────────────────────────────────────────────┐
│                   应用层                         │
│  I/O 模式 (顺序/随机)、块大小、并发度            │
├─────────────────────────────────────────────────┤
│                 文件系统层                        │
│  CoW 开销、B-Tree 缓存、Journal、压缩            │
├─────────────────────────────────────────────────┤
│                  内核层                          │
│  I/O 调度器、页缓存、预读策略                     │
├─────────────────────────────────────────────────┤
│                 硬件层                           │
│  存储设备类型、CPU、内存、总线带宽                │
└─────────────────────────────────────────────────┘

9.1.2 性能基准指标

指标说明单位
IOPS每秒 I/O 操作数ops/s
吞吐量每秒传输数据量MB/s
延迟I/O 操作耗时μs / ms
CPU 使用文件系统操作 CPU 开销%

9.2 挂载选项调优

9.2.1 常用性能挂载选项

# 完整的性能优化挂载
sudo mount -t bcachefs -o \
    noatime,\
    nodiratime,\
    discard,\
    compress=lz4,\
    journal_flush_delay=5000 \
    /dev/sdb /mnt/data

9.2.2 挂载选项详解

选项默认值推荐值说明
noatimeoffon不更新访问时间,减少元数据写入
nodiratimeoffon不更新目录访问时间
discardoffSSD: on启用 TRIM,帮助 SSD 垃圾回收
journal_flush_delay1000ms5000ms延迟日志刷新,合并小写入
gc_reserve_percent8%5-15%GC 保留空间比例
btree_node_size256KB可调B-Tree 节点大小
readahead128KB可调预读窗口大小

9.2.3 noatime 详解

# 默认行为 (atime):
# 每次读取文件都更新 inode 的访问时间
# 导致大量元数据写入

# 推荐: noatime
# 不更新访问时间,性能提升 10-30%

# 验证当前挂载选项
mount | grep bcachefs
# 应该包含 noatime

9.2.4 discard (TRIM) 选项

# SSD 必须启用 discard
# 帮助 SSD 控制器回收已删除数据块

# 检查 SSD TRIM 支持
sudo hdparm -I /dev/sda | grep TRIM

# 如果不支持在线 TRIM,可以定期执行
sudo fstrim /mnt/data
# 或配置 cron 每周执行

9.2.5 Journal 调优

# journal_flush_delay 控制日志刷新频率
# 较大值: 更好的合并,但崩溃时可能丢更多数据
# 较小值: 更安全,但 IOPS 更低

# 默认 1000ms,推荐生产环境 1000-5000ms
sudo mount -t bcachefs -o journal_flush_delay=3000 /dev/sdb /mnt/data

# 使用专用日志设备(NVMe)效果更好

9.3 I/O 调度器调优

9.3.1 I/O 调度器概述

Linux I/O 调度器:

┌──────────────┬─────────────────────────────────┐
│ 调度器       │ 特点                            │
├──────────────┼─────────────────────────────────┤
│ none         │ 直接派发,无调度 (NVMe 推荐)    │
│ mq-deadline  │ 延迟优先 (SSD 推荐)             │
│ bfq          │ 公平队列 (HDD/桌面推荐)         │
│ kyber        │ 双队列 (快速设备推荐)            │
└──────────────┴─────────────────────────────────┘

9.3.2 配置 I/O 调度器

# 查看当前调度器
cat /sys/block/sdb/queue/scheduler
# 输出: [mq-deadline] kyber bfq none

# 设置调度器
# NVMe: none(直接派发,延迟最低)
echo "none" | sudo tee /sys/block/nvme0n1/queue/scheduler

# SATA SSD: mq-deadline
echo "mq-deadline" | sudo tee /sys/block/sda/queue/scheduler

# HDD: bfq(适合旋转磁盘)
echo "bfq" | sudo tee /sys/block/sdb/queue/scheduler

# 永久配置 (udev 规则)
cat << 'EOF' | sudo tee /etc/udev/rules.d/60-io-scheduler.rules
# NVMe
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SSD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
EOF

sudo udevadm control --reload-rules

9.3.3 调度器参数调优

# mq-deadline 参数
echo 150 | sudo tee /sys/block/sda/queue/iosched/read_expire   # 读超时 150ms
echo 1500 | sudo tee /sys/block/sda/queue/iosched/write_expire  # 写超时 1500ms

# bfq 参数
echo 100 | sudo tee /sys/block/sdb/queue/iosched/read_expire    # 读超时 100ms

# 队列深度
echo 32 | sudo tee /sys/block/sda/queue/nr_requests   # SSD
echo 128 | sudo tee /sys/block/nvme0n1/queue/nr_requests  # NVMe

9.4 内核参数调优

9.4.1 虚拟内存参数

# 查看当前参数
sysctl vm.dirty_ratio
sysctl vm.dirty_background_ratio

# 推荐配置
# dirty_ratio: 脏页占内存的最大百分比(触发同步写入)
sudo sysctl -w vm.dirty_ratio=10

# dirty_background_ratio: 脏页后台刷新阈值
sudo sysctl -w vm.dirty_background_ratio=5

# 脏页过期时间(厘秒)
sudo sysctl -w vm.dirty_expire_centisecs=3000

# 脏页写回间隔
sudo sysctl -w vm.dirty_writeback_centisecs=500

# 永久配置
cat << 'EOF' | sudo tee -a /etc/sysctl.d/99-bcachefs.conf
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
EOF

sudo sysctl --system

9.4.2 文件系统缓存

# 查看当前缓存设置
cat /proc/sys/fs/inotify/max_user_watches

# 增加 inotify 监视数(大量文件时需要)
sudo sysctl -w fs.inotify.max_user_watches=524288

# VFS 缓存压力
# 默认 100,降低可以保留更多缓存
sudo sysctl -w vm.vfs_cache_pressure=50

# 永久配置
cat << 'EOF' | sudo tee -a /etc/sysctl.d/99-bcachefs.conf
fs.inotify.max_user_watches = 524288
vm.vfs_cache_pressure = 50
EOF

9.4.3 NUMA 优化

# 多 NUMA 节点系统
numactl --hardware

# 绑定 I/O 到特定 NUMA 节点
numactl --cpunodebind=0 --membind=0 mount -t bcachefs /dev/sdb /mnt/data

9.5 块设备调优

9.5.1 队列参数

# 查看设备队列参数
ls /sys/block/sdb/queue/

# 最大队列深度
cat /sys/block/nvme0n1/queue/nr_requests

# 最大扇区数
cat /sys/block/sdb/queue/max_sectors_kb

# 预读大小
cat /sys/block/sdb/queue/read_ahead_kb

9.5.2 SSD 特定优化

# 启用 SSD 优化
echo 0 | sudo tee /sys/block/sda/queue/rotational  # 确保标记为非旋转

# NVMe 优化
echo 0 | sudo tee /sys/block/nvme0n1/queue/add_random  # 不添加熵
echo 2 | sudo tee /sys/block/nvme0n1/queue/nomerges     # 禁用合并(NVMe 本身很快)

# SATA SSD
echo 512 | sudo tee /sys/block/sda/queue/read_ahead_kb  # 增加预读
echo 512 | sudo tee /sys/block/sda/queue/max_sectors_kb  # 增大最大扇区

9.5.3 HDD 特定优化

# 启用预读(HDD 顺序读很重要)
echo 2048 | sudo tee /sys/block/sdb/queue/read_ahead_kb

# 启用合并
echo 1 | sudo tee /sys/block/sdb/queue/nomerges

# NCQ 队列深度
echo 31 | sudo tee /sys/block/sdb/queue/nr_requests

9.6 Bcachefs 特定调优

9.6.1 B-Tree 节点大小

# 创建时指定 B-Tree 节点大小
# 较大节点: 更少的树遍历,但更多内存
# 较小节点: 更少内存,但更多磁盘 I/O

# 推荐: 256KB (默认)
sudo bcachefs format /dev/sdb --btree_node_size=256K

# 大内存系统可使用更大节点
sudo bcachefs format /dev/sdb --btree_node_size=512K

9.6.2 Bucket 大小优化

# 小文件场景: 使用较小 bucket
sudo bcachefs format /dev/sdb --bucket_size=64K

# 大文件场景: 使用较大 bucket
sudo bcachefs format /dev/sdb --bucket_size=1M

# 查看当前 bucket 大小
sudo bcachefs show-super /dev/sdb | grep bucket

9.6.3 GC 调优

# gc_reserve_percent 控制 GC 触发阈值
# 默认 8%,意味着当空闲空间 < 8% 时触发 GC

# 增加 GC 保留空间(减少 GC 频率)
sudo mount -t bcachefs -o gc_reserve_percent=12 /dev/sdb /mnt/data

# 手动触发 GC
sudo bcachefs fs gc /mnt/data

# 锁定 GC(用于性能测试)
sudo bcachefs fs lock /mnt/data

9.7 基准测试

9.7.1 fio 测试

# 安装 fio
sudo apt install fio  # Debian/Ubuntu
sudo pacman -S fio    # Arch Linux

# 顺序写入测试
fio --name=seq-write \
    --ioengine=libaio \
    --rw=write \
    --bs=1M \
    --size=4G \
    --numjobs=1 \
    --runtime=60 \
    --directory=/mnt/data/fio-test

# 顺序读取测试
fio --name=seq-read \
    --ioengine=libaio \
    --rw=read \
    --bs=1M \
    --size=4G \
    --numjobs=1 \
    --runtime=60 \
    --directory=/mnt/data/fio-test

# 随机读写测试
fio --name=rand-rw \
    --ioengine=libaio \
    --rw=randrw \
    --bs=4k \
    --size=1G \
    --numjobs=4 \
    --runtime=60 \
    --iodepth=32 \
    --directory=/mnt/data/fio-test

# 混合测试脚本
fio --name=bcachefs-bench \
    --ioengine=libaio \
    --directory=/mnt/data/fio-test \
    --group_reporting \
    --bs=4k,64k,1M \
    --rw=randread:randwrite:read:write \
    --size=2G \
    --numjobs=4 \
    --runtime=30 \
    --iodepth=16

9.7.2 dd 测试

# 顺序写入
dd if=/dev/zero of=/mnt/data/testfile bs=1M count=4096 conv=fdatasync

# 顺序读取
dd if=/mnt/data/testfile of=/dev/null bs=1M

# 块大小测试
for bs in 4k 64k 256k 1M; do
    echo "=== Block size: $bs ==="
    dd if=/dev/zero of=/mnt/data/testfile-$bs bs=$bs count=$((4096*1024/$(echo $bs | sed 's/k/*1024/' | sed 's/M/*1048576/'))) conv=fdatasync 2>&1 | tail -1
    rm /mnt/data/testfile-$bs
done

9.7.3 iozone 测试

# 安装 iozone
sudo apt install iozone3

# 运行综合测试
iozone -a -n 512k -g 4G -i 0 -i 1 -i 2 -f /mnt/data/iozone-test -Rb /tmp/iozone-results.xls

# 参数说明:
# -a: 自动模式
# -n: 最小文件大小
# -g: 最大文件大小
# -i 0: 写/重写测试
# -i 1: 读/重读测试
# -i 2: 随机读/写测试

9.7.4 综合基准脚本

#!/bin/bash
# benchmark-bcachefs.sh

set -e

MOUNT="/mnt/data"
TEST_DIR="$MOUNT/benchmark"
RESULT_FILE="/tmp/bcachefs-benchmark-$(date +%Y%m%d).txt"

mkdir -p "$TEST_DIR"

echo "=== Bcachefs 性能基准测试 ===" | tee "$RESULT_FILE"
echo "时间: $(date)" | tee -a "$RESULT_FILE"
echo "设备: $(mount | grep $MOUNT | awk '{print $1}')" | tee -a "$RESULT_FILE"
echo "" | tee -a "$RESULT_FILE"

# 清理缓存
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null

# 测试 1: 顺序写入
echo "--- 测试 1: 顺序写入 (1M block) ---" | tee -a "$RESULT_FILE"
dd if=/dev/zero of="$TEST_DIR/seq-write" bs=1M count=2048 conv=fdatasync 2>&1 | tail -1 | tee -a "$RESULT_FILE"

# 测试 2: 顺序读取
echo "--- 测试 2: 顺序读取 (1M block) ---" | tee -a "$RESULT_FILE"
echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
dd if="$TEST_DIR/seq-write" of=/dev/null bs=1M 2>&1 | tail -1 | tee -a "$RESULT_FILE"

# 测试 3: 随机写入 (4K)
echo "--- 测试 3: 随机写入 (4K block, fio) ---" | tee -a "$RESULT_FILE"
fio --name=rand-write \
    --ioengine=libaio \
    --rw=randwrite \
    --bs=4k \
    --size=512M \
    --numjobs=1 \
    --runtime=30 \
    --directory="$TEST_DIR" \
    --output-format=terse 2>/dev/null | awk -F';' '{print "IOPS: " $8 ", BW: " $7 " KB/s"}' | tee -a "$RESULT_FILE"

# 测试 4: 随机读取 (4K)
echo "--- 测试 4: 随机读取 (4K block, fio) ---" | tee -a "$RESULT_FILE"
fio --name=rand-read \
    --ioengine=libaio \
    --rw=randread \
    --bs=4k \
    --size=512M \
    --numjobs=1 \
    --runtime=30 \
    --directory="$TEST_DIR" \
    --output-format=terse 2>/dev/null | awk -F';' '{print "IOPS: " $8 ", BW: " $7 " KB/s"}' | tee -a "$RESULT_FILE"

# 清理
rm -rf "$TEST_DIR"

echo "" | tee -a "$RESULT_FILE"
echo "=== 测试完成 ===" | tee -a "$RESULT_FILE"
echo "结果保存到: $RESULT_FILE"

9.8 监控工具

9.8.1 iostat

# 安装 sysstat
sudo apt install sysstat

# 实时监控
iostat -x 1

# 输出说明:
# Device   r/s    w/s    rkB/s   wkB/s   await  %util
# sdb      1500   800    60000   32000   0.5    45.2
# 
# r/s: 读 IOPS
# w/s: 写 IOPS
# await: 平均 I/O 等待时间 (ms)
# %util: 设备利用率

9.8.2 iotop

# 安装
sudo apt install iotop

# 监控进程级 I/O
sudo iotop -oP

# 输出显示每个进程的 I/O 使用情况

9.8.3 blktrace

# 安装
sudo apt install blktrace

# 跟踪块设备 I/O
sudo blktrace -d /dev/sdb -o - | blkparse -i -

# 分析 I/O 模式
sudo btt -i sdb.blktrace.0

9.8.4 perf

# 安装
sudo apt install linux-perf

# 监控文件系统相关的内核函数
sudo perf top -g

# 或记录特定事件
sudo perf record -g -a sleep 10
sudo perf report

9.8.5 Bcachefs 特定监控

# 查看 Bcachefs 内核统计
cat /proc/fs/bcachefs/*/internal/*

# 查看挂载选项
cat /proc/fs/bcachefs/*/options

# 内核日志
dmesg | grep bcachefs

9.9 性能问题排查

9.9.1 高延迟

# 检查设备利用率
iostat -x 1
# 如果 %util > 90%,设备可能是瓶颈

# 检查 I/O 调度器
cat /sys/block/sdb/queue/scheduler

# 检查 B-Tree 缓存命中
# 如果缓存命中率低,可能需要更多内存
free -h

9.9.2 低 IOPS

# 检查队列深度
cat /sys/block/sdb/queue/nr_requests

# 检查 I/O 调度器
# none/mq-deadline 通常 IOPS 更高

# 检查是否有 GC 在运行
dmesg | grep -i "gc\|garbage"

9.9.3 低吞吐量

# 检查块大小
# 大块顺序 I/O 吞吐量更高

# 检查是否启用了压缩
# 压缩可能成为瓶颈

# 检查多设备是否均匀
iostat -x 1
# 观察各设备的 wkB/s 是否均衡

9.9.4 CPU 瓶颈

# 检查 CPU 使用
top
# 观察 iowait 和系统 CPU 使用率

# 如果 CPU 成为瓶颈:
# 1. 降低压缩级别
# 2. 切换到 lz4
# 3. 关闭不必要的校验和

9.10 工作负载特定优化

9.10.1 数据库工作负载

# 数据库特点: 随机小块 I/O,低延迟要求

# 推荐配置:
# - 挂载选项: noatime, journal_flush_delay=1000
# - I/O 调度器: none (NVMe) 或 mq-deadline (SSD)
# - 压缩: lz4 或 none
# - 块大小: 4K (InnoDB 默认页大小)

sudo mount -t bcachefs -o \
    noatime,\
    compress=lz4,\
    journal_flush_delay=1000 \
    /dev/nvme0n1 /var/lib/mysql

9.10.2 文件服务器

# 文件服务器特点: 混合 I/O,大块顺序读写

# 推荐配置:
# - 挂载选项: noatime, compress=zstd
# - I/O 调度器: bfq (HDD) 或 mq-deadline (SSD)
# - 预读: 较大值 (1024KB+)
# - 压缩: zstd (节省空间)

sudo mount -t bcachefs -o \
    noatime,\
    compress=zstd \
    /dev/sdb:/dev/sdc /mnt/fileserver

# 增加预读
echo 2048 | sudo tee /sys/block/sdb/queue/read_ahead_kb

9.10.3 开发工作站

# 开发特点: 频繁小文件操作,编译时大量 I/O

# 推荐配置:
# - 挂载选项: noatime, compress=zstd
# - 快照: 启用
# - 预读: 中等 (256KB)

sudo mount -t bcachefs -o \
    noatime,\
    compress=zstd \
    /dev/nvme0n1 /home

9.11 本章小结

优化领域关键参数影响
挂载选项noatime, discard减少元数据写入
I/O 调度器none/mq-deadline/bfq延迟和吞吐量
内核参数dirty_ratio, readahead写入合并和预读
块设备nr_requests, max_sectors_kb队列深度和块大小
Bcachefsbucket_size, gc_reserve空间分配和 GC

扩展阅读


上一章: ← 第 8 章:压缩策略 | 下一章: 第 10 章:Docker 与容器化 →