Đổi bộ lập lịch I/O là một trong những lời khuyên tinh chỉnh phổ biến nhất. Bài này đo xem nó cho bao nhiêu.
Bài kiểm tra
Đây đúng là kịch bản mà bộ lập lịch sinh ra để xử lý: một tiến trình ghi tuần tự liên tục (tác vụ nền), một tiến trình khác đọc khối nhỏ 4 KB ngẫu nhiên (dịch vụ nhạy cảm với độ trễ). Bộ lập lịch tốt phải bảo vệ được kẻ đọc.
Ba lần đo mỗi bộ lập lịch:
| Lần | none |
mq-deadline |
kyber |
|---|---|---|---|
| 1 | 15.726 IOPS, p99 171 µs | 15.702 IOPS, p99 169 µs | 15.190 IOPS, p99 183 µs |
| 2 | 15.430 IOPS, p99 167 µs | 15.424 IOPS, p99 165 µs | 15.900 IOPS, p99 169 µs |
| 3 | 15.310 IOPS, p99 183 µs | 15.591 IOPS, p99 171 µs | 15.879 IOPS, p99 173 µs |
Toàn bộ dải: 15.190 – 15.900 IOPS, p99 165 – 183 µs. Không bộ lập lịch nào tách ra khỏi nhiễu. Thứ hạng còn đổi giữa các lần chạy.
Vì sao chúng bằng nhau
Bộ lập lịch I/O sinh ra để giải hai bài toán của đĩa quay:
- Sắp xếp lại yêu cầu theo vị trí trên đĩa để đầu đọc đi ít nhất — thuật toán "thang máy".
- Chống bỏ đói: giữ một yêu cầu đọc nhỏ khỏi bị chôn dưới hàng nghìn yêu cầu ghi tuần tự.
Bài toán thứ nhất biến mất trên ổ thể rắn: phần 19 đã đo và tuần tự với ngẫu nhiên cho cùng một con số, nên sắp xếp lại chẳng để làm gì.
Bài toán thứ hai gần như biến mất theo: NVMe có hàng nghìn hàng đợi phần cứng và phục vụ chúng song song, nên yêu cầu nhỏ không phải xếp sau yêu cầu lớn.
Đó là lý do none là mặc định cho NVMe trên mọi bản phân phối hiện đại. Nó không phải là "chưa cấu hình" — nó là lựa chọn đúng.
Trên đĩa quay thì hoàn toàn khác, và những lời khuyên bạn đọc được thường ra đời từ thời đó. Nếu hệ thống của bạn còn chạy đĩa quay, bfq hoặc mq-deadline vẫn tạo khác biệt lớn. Nhân trên máy tôi đo không có bfq nên tôi không đo được nó.
Nút thật sự có tác dụng
Cùng thiết bị, cùng phép đo đọc tuần tự qua bộ đệm trang, chỉ đổi cửa sổ đọc trước:
read_ahead_kb |
Thông lượng |
|---|---|
| 4 | 375,9 MB/s |
| 32 | 1.398,6 MB/s |
| 128 (mặc định) | 2.510,5 MB/s |
| 512 | 3.370,8 MB/s |
| 2.048 | 4.026,8 MB/s |
10,7 lần.
Nút được người ta chỉnh nhiều nhất cho 0%. Nút gần như không ai đụng tới cho 10,7 lần.
Không phải vì bộ lập lịch vô dụng, mà vì phần cứng đã đổi và nó không còn là chỗ tắc nghẽn. Chỗ tắc nghẽn bây giờ là số yêu cầu đang bay — đúng như phần 19 đã đo với iodepth. Đọc trước là cách nhân tự tạo ra độ sâu hàng đợi thay cho bạn, và cửa sổ đọc trước quyết định nó tạo được bao nhiêu.
Đọc trước lớn không miễn phí
Cửa sổ 2 MB nghĩa là mỗi lần chạm vào một chỗ mới, nhân kéo về 2 MB. Với truy cập tuần tự thì tuyệt vời. Với truy cập ngẫu nhiên thì:
- Lãng phí băng thông cho dữ liệu không bao giờ dùng tới.
- Đẩy dữ liệu đang nóng ra khỏi bộ đệm trang.
Nên quy tắc là chỉnh theo tải, không chỉnh theo thiết bị:
| Tải | read_ahead_kb |
|---|---|
| Quét bảng lớn, sao lưu, phân tích dữ liệu | 1024 – 4096 |
| Mặc định, tải hỗn hợp | 128 |
| Cơ sở dữ liệu OLTP, truy cập ngẫu nhiên | 16 – 64 |
blockdev --setra 2048 /dev/nvme0n1 # don vi 512 byte -> 1 MB
echo 1024 > /sys/block/nvme0n1/queue/read_ahead_kb # don vi KB
Hai lệnh này chỉnh cùng một thứ với hai đơn vị khác nhau — một nguồn nhầm lẫn quen thuộc.
Đặt bền qua khởi động lại bằng udev:
# /etc/udev/rules.d/60-readahead.rules
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/read_ahead_kb}="1024"
Hai nút khác đáng biết
cat /sys/block/nvme0n1/queue/nr_requests # do sau hang doi cua tang khoi
cat /sys/block/nvme0n1/queue/nomerges # 0 = cho gop yeu cau lien ke
cat /sys/block/nvme0n1/queue/rotational # 0 = the ran, 1 = dia quay
rotational là cờ mà nhân dùng để chọn mặc định. Máy ảo đôi khi báo sai — nếu nó là 1 trên một ổ SSD, nhân sẽ chọn bộ lập lịch và cửa sổ đọc trước của đĩa quay, và đó là một lỗi cấu hình thật sự đáng sửa.
Giới hạn của phép đo này
Thiết bị tôi đo là tệp loop nằm trên overlay nằm trong máy ảo. Bộ lập lịch của nhân trong máy ảo điều phối một hàng đợi ảo, còn việc sắp xếp thật diễn ra ở tầng dưới mà nó không với tới.
Nghĩa là kết quả "ba bộ lập lịch bằng nhau" được củng cố bởi bố trí này chứ không bị nó phủ định — trong container, bộ lập lịch còn ít việc hơn nữa. Nếu bạn chạy container, đó chính là tình huống của bạn.
Trên kim loại trần với đĩa quay, hãy tự đo lại. Đó cũng là điểm chung của cả loạt bài này: mọi con số đều thuộc về một máy cụ thể, và cái đáng mang đi là cách đo.
Thử ba mươi giây
Xem thiết bị của bạn đang dùng gì và thử đổi cửa sổ đọc trước:
for d in /sys/block/*/queue; do
dev=$(echo $d | cut -d/ -f4)
case $dev in loop*|ram*) continue;; esac
printf "%-10s rotational=%s ra=%-6s sched=%s\n" "$dev" \
"$(cat $d/rotational)" "$(cat $d/read_ahead_kb)" "$(cat $d/scheduler)"
done
# thu tang cua so doc truoc roi do lai (can root, doi ten thiet bi)
d=nvme0n1
cu=$(cat /sys/block/$d/queue/read_ahead_kb)
for ra in 128 1024; do
echo $ra > /sys/block/$d/queue/read_ahead_kb
sync; echo 3 > /proc/sys/vm/drop_caches
printf "ra=%-6s " $ra
dd if=/duong/dan/tep-lon of=/dev/null bs=4k count=200000 2>&1 | tail -1
done
echo $cu > /sys/block/$d/queue/read_ahead_kb
Nếu dòng ra=1024 nhanh hơn đáng kể và tải của bạn là quét tuần tự, bạn vừa tìm được một cải thiện lớn hơn mọi thứ mà việc đổi bộ lập lịch có thể cho.
Phần sau: theo dõi I/O trong thực tế — iostat, iotop và cách đọc chúng cho đúng.