Vắt kiệt hiệu năng NVMe cho MySQL: Từ cấu hình innodb_io_capacity đến tinh chỉnh Kernel

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

Câu chuyện chiếc Ferrari chạy trong ngõ nhỏ

Tôi từng xử lý hệ thống log cho một sàn thương mại điện tử lớn với lưu lượng khoảng 50.000 request/giây. Khi hệ thống nghẽn I/O, sếp đã chi hàng nghìn USD nâng cấp sang dàn NVMe Samsung Enterprise. Cả team đều đinh ninh tốc độ sẽ tăng vọt vì thông số IOPS của NVMe cao gấp hàng chục lần SSD SATA cũ.

Thực tế lại là một gáo nước lạnh. Sau khi chuyển data, chỉ số iowait vẫn đỏ rực, ứng dụng vẫn phản hồi chậm chạp. Hóa ra, cài đặt MySQL mặc định giống như việc bạn lái Ferrari trong ngõ nhỏ với giới hạn 20km/h. Nếu không biết cách “mở khóa”, phần cứng xịn đến mấy cũng chỉ để làm cảnh.

Tại sao MySQL mặc định lại làm phí tiền mua ổ NVMe?

Lý do rất đơn giản: Các tham số mặc định của InnoDB được viết từ thời ổ cứng HDD còn thống trị.

  • Giới hạn IOPS quá thấp: innodb_io_capacity mặc định chỉ là 200. Trong khi đó, một ổ NVMe tầm trung hiện nay dễ dàng đạt 300.000 – 500.000 IOPS.
  • Cơ chế Flush lỗi thời: Cách InnoDB đẩy dữ liệu xuống đĩa quá rụt rè, khiến ổ cứng thì ngồi chơi mà CPU lại phải chờ đợi I/O.
  • Hàng đợi Kernel: Linux mặc định thường cố gắng sắp xếp các yêu cầu I/O để tối ưu cho đầu đọc cơ học, điều này hoàn toàn thừa thãi với chip nhớ NVMe.

Bước 1: Ép InnoDB phải làm việc hết công suất

Mở file my.cnf lên. Đây là nơi bạn giải phóng sức mạnh cho phần cứng.

Cấu hình innodb_io_capacity

Tham số này định nghĩa khả năng xử lý I/O của hệ thống lưu trữ. Với NVMe, hãy mạnh dạn tăng con số này lên. Đừng dùng mức 200 yếu ớt nữa.

[mysqld]
# Với ổ NVMe đơn lẻ (ví dụ Samsung 980 Pro/PM9A1)
innodb_io_capacity = 5000
innodb_io_capacity_max = 10000

# Nếu chạy RAID 0 NVMe hoặc dòng chuyên dụng Enterprise
# innodb_io_capacity = 20000
# innodb_io_capacity_max = 40000

Tắt tính năng innodb_flush_neighbors

Trên HDD, việc ghi các block dữ liệu liền kề giúp giảm quãng đường di chuyển của đầu đọc. Với NVMe, việc này chỉ làm tốn tài nguyên CPU vô ích. Hãy tắt nó ngay lập tức.

innodb_flush_neighbors = 0

Sử dụng innodb_flush_method = O_DIRECT

Mặc định, dữ liệu bị cache hai lần tại InnoDB Buffer Pool và OS Page Cache. O_DIRECT giúp MySQL ghi thẳng xuống ổ đĩa, giải phóng RAM và giảm độ trễ ghi xuống mức tối thiểu.

innodb_flush_method = O_DIRECT

Bước 2: Tinh chỉnh Linux Kernel để “mở đường” cho dữ liệu

Hệ điều hành là cầu nối giữa MySQL và phần cứng. Nếu cầu hẹp, xe dù mạnh cũng không thể đi nhanh.

Thay đổi I/O Scheduler

Các scheduler như cfq hay deadline sinh ra để phục vụ ổ đĩa quay. Với NVMe, bạn nên chuyển sang none để bỏ qua lớp sắp xếp trung gian của Kernel, giúp giảm latency đáng kể.

# Kiểm tra (thay nvme0n1 bằng tên ổ thực tế của bạn)
cat /sys/block/nvme0n1/queue/scheduler

# Chuyển sang none để đạt tốc độ cao nhất
echo none > /sys/block/nvme0n1/queue/scheduler

Tăng hàng đợi nr_requests

NVMe cực giỏi trong việc xử lý song song. Hãy cho phép hệ điều hành đẩy nhiều yêu cầu hơn vào hàng đợi cùng một lúc.

echo 1024 > /sys/block/nvme0n1/queue/nr_requests

Cấu hình Mount noatime

Mỗi khi bạn SELECT, Linux mặc định sẽ ghi lại thời gian truy cập file. Với database, việc này cực kỳ lãng phí. Thêm noatime vào /etc/fstab để triệt tiêu các thao tác ghi thừa này.

/dev/nvme0n1p1  /var/lib/mysql  ext4  defaults,noatime  0 2

Lỗi kinh điển: Quên cấu hình innodb_log_file_size

Tôi từng gặp trường hợp dù đã tối ưu I/O cực tốt nhưng tốc độ ghi vẫn bị khựng mỗi 5 phút. Sau khi kiểm tra, thủ phạm là innodb_log_file_size chỉ để 128MB.

Khi file log đầy, MySQL sẽ dừng mọi hoạt động để thực hiện checkpoint. Với NVMe, bạn nên để log file tối thiểu 1GB – 2GB. Điều này giúp kéo dài khoảng cách giữa các lần checkpoint, giúp hiệu năng ổn định hơn dưới tải cao.

innodb_log_file_size = 2G
innodb_log_files_in_group = 2

Tối ưu hóa là một hành trình, không phải đích đến

Đừng copy paste mọi thông số vào server production ngay. Hãy thực hiện theo quy trình 4 bước:

  1. Benchmark: Dùng fio đo IOPS thực tế của ổ cứng trước khi cấu hình.
  2. Tinh chỉnh Kernel: Ưu tiên none scheduler và noatime.
  3. Cấu hình MySQL: Tập trung vào io_capacityO_DIRECT.
  4. Giám sát: Chạy iostat -x 1. Nếu cột %util dưới 60% mà ứng dụng vẫn chậm, bạn vẫn còn dư địa để tăng innodb_io_capacity.

Cấu hình mặc định là kẻ thù của hiệu năng. Hãy mạnh dạn thay đổi để tận dụng từng đồng chi phí bạn đã bỏ ra cho ổ NVMe. Kết quả phản hồi từ ứng dụng sẽ chứng minh bạn đúng!

Share: