FastAPI và Python worker: nghẽn ở CPU hay ở ổ đĩa

Chia sẻ bài viết

Mục lục
FastAPI và Python worker: nghẽn ở CPU hay ở ổ đĩa

Trả lời nhanh: Phải phân biệt hai loại tải. Tác vụ chờ như gọi API bên ngoài hoặc truy vấn database thì CPU rảnh, nút thắt nằm ở độ trễ và ở ổ đĩa của database. Tác vụ tính toán như xử lý ảnh hay phân tích dữ liệu thì CPU là nút thắt, và do đặc thù của Python, thêm nhân chỉ giúp khi bạn chạy nhiều tiến trình chứ không phải nhiều luồng. Đo bằng top: CPU thấp mà phản hồi chậm nghĩa là đang chờ, không phải thiếu CPU.

Ứng dụng chậm, nhìn vào thấy CPU chỉ dùng mười phần trăm. Thêm nhân không giải quyết được gì, vì vấn đề không nằm ở CPU.

Tóm tắt nhanh
  • Tác vụ chờ và tác vụ tính toán cần cấu hình khác hẳn nhau.
  • CPU thấp mà chậm nghĩa là đang chờ database hoặc dịch vụ ngoài.
  • Với Python, chạy nhiều tiến trình mới tận dụng được nhiều nhân.
  • Số worker hợp lý thường bằng số nhân cộng một, không phải càng nhiều càng tốt.

Phân biệt hai loại tải

Loại tảiVí dụNút thắtNên tăng gì
ChờGọi API, truy vấn database, đọc fileĐộ trễ và ổ đĩaỔ nhanh, tối ưu truy vấn
Tính toánXử lý ảnh, phân tích dữ liệu, mã hoáCPUXung nhịp và số nhân

Phần lớn ứng dụng web thuộc loại chờ. Đó là lý do nhiều người nâng CPU mà không thấy khác biệt.

Đo để biết mình thuộc loại nào

top          # xem mức dùng CPU và chỉ số wa
iostat -x 2  # xem %util của ổ

Ba kịch bản:

  • CPU thấp, phản hồi chậm: đang chờ. Tìm ở database hoặc dịch vụ ngoài.
  • CPU cao ở một nhân: tác vụ tính toán chạy đơn tiến trình, cần chạy nhiều tiến trình hơn hoặc cần xung cao hơn.
  • %util của ổ gần 100: ổ đĩa là nút thắt.

Chọn số worker cho đúng

Công thức thường dùng cho tải hỗn hợp: số worker bằng số nhân cộng một. Máy bốn nhân thì năm worker.

Đặt quá nhiều worker phản tác dụng: mỗi worker chiếm một phần RAM, và khi tổng vượt RAM máy thì hệ thống bắt đầu dùng swap, lúc đó mọi thứ chậm đi hàng chục lần. Nhiều trường hợp giảm số worker lại làm ứng dụng nhanh hơn.

Với tải chờ nhiều, cách hiệu quả hơn là dùng mã bất đồng bộ để một worker phục vụ được nhiều yêu cầu đang chờ, thay vì tăng số worker.

Khi worker xử lý file

Worker xử lý ảnh, chuyển đổi tài liệu hay tạo báo cáo đều ghi rất nhiều file tạm. Đây là loại tải vừa nặng CPU vừa nặng ổ đĩa, và là trường hợp hiếm mà nâng cả hai đều thấy khác biệt.

Mẹo nhỏ: đặt thư mục file tạm trên ổ nhanh nhất và dọn định kỳ. File tạm không được dọn là nguyên nhân đầy ổ phổ biến trong hệ thống có worker.

Đo để biết mình thuộc loại tải nào

# Trong luc he thong dang tai that
top -bn1 | head -5
vmstat 2 30    # cot us cao la chay vi xu ly; cot wa cao la cho o dia
iostat -x 2 10 # %util gan 100 la nghen o dia

# Xem tien trinh nao dang lam gi
py-spy dump --pid PID          # neu da cai py-spy
cat /proc/PID/io               # tong byte doc ghi

# Do thoi gian phan hoi tu ben ngoai
for i in $(seq 1 30); do
  curl -o /dev/null -s -w "%{time_total}\n" http://127.0.0.1:8000/health
done | sort -n | awk '{a[NR]=$1} END {print "trung vi", a[int(NR/2)], "cham nhat", a[NR]}'
Bạn thấyLoại tảiCách chọn số tiến trình xử lý
Vi xử lý gần 100%, chờ ổ đĩa thấpNặng tính toánSố tiến trình bằng số nhân, không hơn
Vi xử lý thấp, chờ ổ đĩa hoặc chờ mạng caoNặng chờ vào raSố tiến trình cao hơn số nhân, và dùng cơ chế bất đồng bộ
Cả hai đều thấp mà vẫn chậmNghẽn ở nơi khácKiểm cơ sở dữ liệu và dịch vụ bên ngoài
Bộ nhớ đầy, có tráo trangThiếu bộ nhớGiảm số tiến trình hoặc thêm RAM

Chọn số tiến trình cho đúng

  • Việc nặng tính toán: số tiến trình bằng số nhân. Nhiều hơn chỉ làm hệ thống bận chuyển qua lại giữa các tiến trình.
  • Việc chủ yếu chờ cơ sở dữ liệu hoặc dịch vụ ngoài: dùng cơ chế bất đồng bộ trong một tiến trình sẽ hiệu quả hơn nhiều so với tăng số tiến trình.
  • Mỗi tiến trình chiếm bộ nhớ riêng. Nhân số tiến trình với bộ nhớ mỗi cái, cộng phần dư cho hệ điều hành, rồi so với bộ nhớ máy.
  • Đừng đặt số tiến trình theo công thức chép trên mạng. Đo bằng ba lệnh ở trên rồi điều chỉnh, vì mỗi ứng dụng một khác.

Khi tiến trình xử lý tệp

  • Đây là loại tải nặng ổ đĩa, và ổ NVMe tạo khác biệt rõ. Xem VPS NVMe.
  • Đừng để tệp tạm nằm cùng chỗ với dữ liệu quan trọng, vì tệp tạm làm đầy ổ rất nhanh.
  • Đặt giới hạn kích thước tệp tải lên ở cả lớp gác cổng lẫn ứng dụng, và đặt hai nơi cùng một con số.
  • Với việc xử lý dài, tách ra hàng đợi thay vì xử lý ngay trong lượt gọi. Người dùng không phải chờ, và bạn kiểm soát được số việc chạy song song.

Câu hỏi thường gặp

Vì sao thêm CPU mà ứng dụng không nhanh hơn?

Vì phần lớn thời gian nó đang chờ database hoặc dịch vụ ngoài chứ không tính toán. Kiểm tra bằng top, CPU thấp mà chậm là dấu hiệu rõ.

Nên chạy bao nhiêu worker?

Thường bằng số nhân cộng một cho tải hỗn hợp. Đặt quá nhiều làm tràn RAM và phản tác dụng.

Ổ NVMe giúp gì cho ứng dụng Python?

Giúp nhiều nếu ứng dụng đọc ghi file hoặc truy vấn database trên cùng máy. Với ứng dụng chỉ gọi API bên ngoài thì ít ảnh hưởng.

Nên tách worker ra máy riêng không?

Nên khi worker chạy tác vụ nặng và bạn không muốn nó làm chậm phần phục vụ người dùng. Dự án nhỏ thì để chung vẫn ổn nếu đặt hạn mức tài nguyên.

Hạ tầng NVMe Enterprise của TND
VPS ổ Enterprise U.2 NVMe, CPU xung cao, đặt tại Việt Nam
Truy vấn SQL, PHP dựng trang, build Next.js hay đóng gói app iOS và Android: phần lớn các bước này chạy đơn luồng và đập liên tục vào ổ đĩa. Nghẽn nằm ở tốc độ đọc ghi và xung nhịp CPU, không phải ở việc có thật nhiều nhân. Ổ Enterprise U.2 NVMe chuẩn máy chủ chạy RAID 10 cho IOPS đọc ghi ngẫu nhiên gấp 5 đến 7 lần ổ SATA SSD, khác hẳn NVMe M.2 phổ thông. Chạy trên chip Intel Xeon Platinum dòng xung cao, backup tự động hàng tuần, bàn giao trong 5 phút.
Xem VPS ổ Enterprise U.2 NVMe tại Việt Nam

Bài viết liên quan