Mở đầu sê-ri năm mươi phần về hiệu năng Linux. Bài này dựng bốn kiểu nghẽn khác nhau rồi đo dấu vết của từng cái.

Bảng vmstat cho bốn tình huống và cột quyết định của mỗi loại

Bốn sự cố, bốn dấu vân tay

Tình huống r b us sy wa cs
Nền (không tải) 1 0 2 2 0 14.429
Nghẽn CPU 15 0 52 2 0 18.176
Nghẽn đĩa 5 36 1 2 94 17.977
Nghẽn bộ nhớ 21 0 39 2 1 15.343
Nghẽn chuyển ngữ cảnh 12 0 2 44 0 964.760

Tất cả từ một lệnh:

vmstat 1

Ý nghĩa các cột:

  • r — số tiến trình đang đợi CPU. Đây không phải số tiến trình đang chạy.
  • b — số tiến trình đang đợi I/O, ở trạng thái ngủ không ngắt được.
  • us — phần trăm CPU chạy mã người dùng.
  • sy — phần trăm CPU chạy trong nhân.
  • wa — phần trăm CPU rảnh nhưng đang chờ đĩa.
  • cs — số lần chuyển ngữ cảnh mỗi giây.

Cột quyết định cho từng loại

Nghẽn CPU: r cao, wa = 0.

15 tiến trình xếp hàng chờ CPU trên máy 16 nhân, và không ai chờ đĩa. Ứng dụng đang thực sự tính toán. Thêm nhân sẽ giúp.

Nghẽn đĩa: b cao, wa = 94, us gần 0.

36 tiến trình bị chặn ở I/O. CPU gần như không làm gì — nó ngồi chờ. Thêm nhân không giúp gì cả.

Nghẽn bộ nhớ: r cao, free tụt xuống 195 MB, bi > 0.

Cần đọc thêm cột từ free: bộ nhớ trống còn 195 MB trên 15.971 MB. Kernel bắt đầu thu hồi bộ đệm liên tục, và bi (block in) khác 0 nghĩa là dữ liệu đang được đọc lại từ đĩa vì nó vừa bị đẩy ra.

Nghẽn khoá / chuyển ngữ cảnh: cs gấp 67 lần, sy cao mà us thấp.

964.760 lần chuyển ngữ cảnh mỗi giây so với 14.429 lúc nền.

Hai dòng đáng chú ý nhất

Nghẽn đĩa cho id = 3%.

Nhìn nhanh vào "CPU rảnh 3%" thì kết luận là CPU đang bận. Sai — 94% thời gian đó CPU ngồi không chờ đĩa trả lời.

Đây là hiểu nhầm phổ biến nhất khi đọc số liệu hệ thống, và nó dẫn tới quyết định sai: mua thêm CPU cho một hệ thống nghẽn đĩa.

Quy tắc: luôn đọc wa cùng với id. id thấp và wa cao nghĩa là "chờ", không phải "bận".

Nghẽn chuyển ngữ cảnh cho us = 2%, sy = 44%.

CPU bận thật, nhưng không bận với công việc của ứng dụng. Nó bận với việc lưu và khôi phục trạng thái mỗi lần chuyển từ tiến trình này sang tiến trình khác.

Trong thực tế, đây thường là dấu hiệu của tranh chấp khoá: nhiều luồng cùng chờ một khoá, mỗi lần khoá được nhả là một cơn bão đánh thức. Thêm luồng làm nó tệ hơn.

Quy trình đọc trong ba mươi giây

Nhìn theo thứ tự này:

  1. wa có cao không? Có → nghẽn đĩa. Dừng ở đây, đừng nhìn CPU.
  2. b có lớn hơn 0 lâu không? Có → cũng là đĩa, hoặc mạng NFS.
  3. si/so có khác 0 không? Có → đang tráo đổi trang nhớ, tức nghẽn bộ nhớ nặng.
  4. free có tụt sát 0 và bi cao không? Có → nghẽn bộ nhớ nhẹ hơn: kernel đang thu hồi bộ đệm.
  5. cs có bất thường cao không? Có → tranh chấp khoá hoặc quá nhiều luồng.
  6. Còn lại, r cao và us cao → nghẽn CPU thật.

Thứ tự này quan trọng: các nguyên nhân ở trên giả trang thành nguyên nhân ở dưới. Một hệ thống nghẽn đĩa trông giống hệt một hệ thống nghẽn CPU nếu bạn chỉ nhìn id.

Vì sao bốn tài nguyên chứ không phải hai

Người ta thường nghĩ về "CPU và RAM". Nhưng đo được ở trên: hai trong bốn tình huống không liên quan tới lượng CPU hay lượng RAM.

  • Nghẽn đĩa là về băng thông và độ trễ của thiết bị lưu trữ.
  • Nghẽn chuyển ngữ cảnh là về cấu trúc chương trình — quá nhiều luồng tranh nhau.

Cả hai đều không sửa được bằng cách mua thêm phần cứng cùng loại. Đó là lý do phải phân biệt được bốn loại trước khi làm bất cứ điều gì.

Một lưu ý về container

Số liệu ở trên đo trong container, nhưng vmstat đọc /proc của nhân, mà nhân thì dùng chung. Nghĩa là bạn thấy tình trạng của cả máy, không phải của riêng container.

Đây vừa là hạn chế vừa là tính năng: bạn không đo được container mình bằng vmstat, nhưng bạn thấy được hàng xóm ồn ào. Với ứng dụng chạy trong container, phần sau của sê-ri sẽ đo cách đọc đúng số liệu của riêng cgroup mình.

Thử ba mươi giây

Chạy trên máy đang có vấn đề:

vmstat 1 10

Bỏ dòng đầu — nó là trung bình từ lúc khởi động, không phải hiện tại. Nhìn chín dòng còn lại theo quy trình sáu bước ở trên.

Và nếu muốn một lệnh cho biết ngay nên nhìn đâu:

vmstat 1 5 | tail -4 | awk '{
  # cot: 12=cs  13=us  14=sy  15=id  16=wa
  if ($16 > 20)            print "-> nghen DIA (wa=" $16 "%)";
  else if ($12 > 100000)   print "-> nghen CHUYEN NGU CANH (cs=" $12 ")";
  else if ($13 > 50)       print "-> nghen CPU nguoi dung (us=" $13 "%)";
  else if ($14 > 30)       print "-> CPU trong NHAN cao (sy=" $14 "%)";
  else                     print "-> khong thay nghen ro rang";
}'

Nó không thay thế được việc đọc kỹ, nhưng nó chỉ đúng chỗ để bắt đầu — và đó thường là phần khó nhất.

Phần sau: load average — vì sao nó không phải phần trăm CPU, và đọc nó thế nào cho đúng.