Nhận thức về NUMA và ghim CPU cho máy chủ chuyên dụng
16 phút đọc - 16 tháng 6, 2026

Cách kiểm tra cấu trúc NUMA và phân bổ các tác vụ Linux vào các lõi và bộ nhớ phù hợp. Bao gồm numactl, taskset, systemd, cài đặt BIOS và các chiến lược dành riêng cho từng loại tác vụ.
Nhận thức về NUMA và ghim CPU cho máy chủ chuyên dụng
Trên bất kỳ máy chủ đa ổ cắm nào, vị trí chạy của một quy trình và vị trí lưu trữ bộ nhớ của quy trình đó là hai vấn đề khác nhau, và việc khiến chúng mất đồng bộ là một trong những cách dễ nhất để làm giảm hiệu suất. Nhận thức về NUMA và ghim CPU là hai công cụ giúp khắc phục vấn đề này. Bài viết này trình bày cách thức hoạt động của NUMA, cách kiểm tra trên Linux và cách ghim khối lượng công việc một cách chính xác cho cơ sở dữ liệu, đào tạo AI và các dịch vụ nhạy cảm với độ trễ.
Cách thức hoạt động của NUMA trên các máy chủ đa ổ cắm
Một nút NUMA (Non-Uniform Memory Access) là một nhóm các lõi CPU được liên kết với một khối RAM cục bộ thông qua bộ điều khiển bộ nhớ chuyên dụng. Trên một máy chủ hai ổ cắm, bạn thường có hai nút. Bất kỳ lõi nào cũng có thể truy cập bất kỳ địa chỉ nào, nhưng thời gian truy cập cục bộ khoảng 80 ns, trong khi việc di chuyển qua socket khác qua Intel UPI hoặc AMD Infinity Fabric mất khoảng 130–150 ns. Trên các hệ thống lớn hơn với nhiều socket, trường hợp xấu nhất có thể vượt quá 250 ns.
Băng thông cũng tuân theo mô hình tương tự. Một hệ thống Sapphire Rapids hai khe cắm có thể duy trì khoảng 600 GB/s khi các lõi truy cập bộ nhớ cục bộ, nhưng liên kết giữa các khe cắm chỉ bằng một phần nhỏ con số đó, do đó lưu lượng đi qua nó nhanh chóng bị tắc nghẽn. Các bộ xử lý nhiều lõi làm cho vấn đề này trở nên phức tạp hơn: Công nghệ Sub-NUMA Clustering (SNC) của Intel và Nodes Per Socket (NPS) của AMD chia mỗi khe cắm thành nhiều vùng NUMA, do đó một hệ thống "hai khe cắm" có thể dễ dàng hiển thị bốn hoặc tám nút cho Linux.
Nếu không có nhận thức về NUMA, trình điều phối Linux sẽ di chuyển một luồng giữa các socket trong khi tập dữ liệu làm việc của nó vẫn ở trên nút ban đầu. Mọi truy cập tiếp theo đều trở thành truy cập từ xa. Triệu chứng rõ ràng là tỷ lệ sử dụng CPU cao nhưng thông lượng thực tế thấp, vì các lõi đang dành thời gian chờ đợi bộ nhớ. Các thiết bị I/O làm tình trạng này trở nên tồi tệ hơn. GPU hoặc NIC được kết nối với một gốc PCIe cụ thể, thuộc về một nút NUMA. Nếu quá trình cung cấp dữ liệu cho nó chạy trên socket khác, mọi chuyển giao DMA sẽ đi qua kết nối liên kết.
Kiểm tra cấu trúc NUMA trên Linux
Bốn công cụ này bao quát gần như mọi thứ bạn cần:
lscpuđể xem tóm tắt nhanh về socket và node.numactl --hardwaređể xem tổng dung lượng bộ nhớ của các nút và ma trận khoảng cách giữa các nút.numastatđể xem số lần trúng/trượt theo từng quá trình.lstopo(từ hwloc) để xem cấu trúc bộ nhớ đệm và tính cục bộ của thiết bị PCIe.
Bắt đầu với numactl --hardware. Nó liệt kê từng nút, các lõi và bộ nhớ thuộc về nó, cùng với ma trận khoảng cách. Giá trị 10 là cục bộ, 20+ là từ xa. Nếu bạn thấy một nút duy nhất trên một hộp đa ổ cắm, BIOS của bạn đã bật Node Interleaving và đang ẩn cấu trúc liên kết, hãy khắc phục điều đó trước (xem bên dưới).
Đối với một quy trình cụ thể, numastat -p <PID> phân tích vị trí bộ nhớ của nó thực sự được phân bổ. Có bốn bộ đếm quan trọng:
numa_hit: bộ nhớ được phân bổ trên nút dự định. Bạn muốn giá trị này cao.numa_miss: nút dự định đã đầy, việc phân bổ tràn sang nơi khác.numa_foreign: một nút khác đã cố gắng phân bổ cục bộ nhưng không thể, cho thấy áp lực bộ nhớ.other_node: các trang được phân bổ trên một nút khác với nơi quá trình đang chạy. Giá trị cao ở đây là dấu hiệu điển hình của việc ghim (pinning) kém.
Đối với các tác vụ GPU hoặc NIC, hãy chạy lstopo-no-graphics và xem mỗi thiết bị PCIe được kết nối với nút NUMA nào. Nếu các lõi điều khiển thiết bị nằm trên nút khác, đó là điều đầu tiên cần khắc phục.
Chính sách ghim CPU và bộ nhớ
Ghim CPU (hoặc CPU affinity) gắn một quá trình với các lõi cụ thể để bộ lập lịch không thể di chuyển nó. Chỉ riêng điều đó là chưa đủ, vì Linux sử dụng chính sách bộ nhớ first-touch theo mặc định: các trang được phân bổ trên bất kỳ nút nào ghi vào chúng trước tiên. Nếu một luồng bắt đầu trên nút sai trước khi được ghim, bộ nhớ của nó sẽ vẫn ở đó. Bạn cần kiểm soát cả vị trí và phân bổ cùng nhau.
Có ba công cụ đáp ứng các trường hợp phổ biến:
| Công cụ | Kiểm soát | Sử dụng cho |
|---|---|---|
taskset | Chỉ dành cho lõi CPU | Gán nhanh một lần cho một quy trình hiện có |
numactl | Lõi CPU và bộ nhớ | Khởi chạy khối lượng công việc với tính cục bộ nghiêm ngặt |
| systemd | Lõi CPU và bộ nhớ, duy trì | Các dịch vụ cần được cố định qua các lần khởi động lại |
numactl hỗ trợ bốn chính sách bộ nhớ:
--membind=N: chỉ phân bổ trên nút N, báo lỗi nếu đầy.--preferred=N: ưu tiên nút N, chuyển sang các nút khác nếu cần.--interleave=all: phân bổ luân phiên giữa các nút để phân phối băng thông đều.--localalloc: phân bổ trên bất kỳ nút nào mà CPU đang chạy.
Gắn cố định khối lượng công việc vào một nút
Đầu tiên, xác định các lõi thuộc về nút mục tiêu của bạn:
numactl --hardwareSau đó, khởi chạy ứng dụng được gắn với nút đó cho cả lõi và bộ nhớ:
numactl --cpunodebind=0 --membind=0 ./your_applicationĐối với một tiến trình đang chạy, điều chỉnh độ ưu tiên CPU bằng taskset:
taskset -cp 0-7 <PID>Để đảm bảo nó tồn tại sau khi khởi động lại, hãy thiết lập trong đơn vị systemd:
[Service]
CPUAffinity=0-7
NUMAPolicy=bind
NUMAMask=0Tải lại và khởi động lại:
sudo systemctl daemon-reload && sudo systemctl restart <service>Khi bạn gán thủ công, hãy tắt bộ cân bằng tự động của kernel để nó không can thiệp vào việc gán của bạn:
sysctl -w kernel.numa_balancing=0Thêm vào /etc/sysctl.conf để duy trì. Sau đó, xác minh bằng numastat -p <PID> trong vài phút với tải công việc thực tế. Nếu other_node giữ ở mức gần bằng không, thì việc ghim đang có hiệu lực.
Lựa chọn chiến lược dựa trên khối lượng công việc
Chính sách phù hợp phụ thuộc vào việc khối lượng công việc của bạn được lợi hơn từ độ trễ thấp hay từ băng thông tổng hợp trên tất cả các nút.
| Tải công việc | Chính sách | Tại sao |
|---|---|---|
| Cơ sở dữ liệu (PostgreSQL, MySQL, SQL Server) | --cpunodebind + --membind | Bộ đệm chia sẻ lớn, các đường dẫn truy vấn nhạy cảm với độ trễ |
| Bộ nhớ đệm trong bộ nhớ (Redis, Memcached) | Kết nối đơn nút | Mọi thứ đều truy cập RAM, độ trễ từ xa xuất hiện ngay lập tức |
| Huấn luyện và suy luận AI/ML | Gắn kết với nút NUMA của GPU | Tránh việc truyền tensor qua các đường dẫn PCIe |
| Phân tích (Spark, Elasticsearch) | --interleave=all | Bộ dữ liệu làm việc lớn cần băng thông trên tất cả các nút |
| API nhạy cảm với độ trễ, giao dịch | Gắn chặt + Ưu tiên IRQ | Khả năng dự đoán quan trọng hơn thông lượng đỉnh |
| Tải nặng về mạng (RoCEv2, InfiniBand) | Gắn pin vào nút NUMA của NIC, dành riêng các lõi cho IRQ | Giữ việc xử lý ngắt tại địa phương và tránh ảnh hưởng đến các luồng ứng dụng |
Đối với các tác vụ GPU cụ thể, hãy chạy lstopo để xác định nút NUMA mà GPU nằm trên đó, sau đó khởi chạy quá trình đào tạo hoặc suy luận với numactl --cpunodebind=N --membind=N cho cùng N đó. Đây là một trong những cách dễ dàng nhất để đạt được hiệu quả trên máy chủ GPU nhiều ổ cắm, vì vị trí của bộ lập lịch mặc định thường không chính xác.
Đối với các tác vụ HPC và MPI trải dài trên cả hai socket, ghim mỗi rank vào một nút duy nhất bằng localalloc thay vì xen kẽ mọi thứ. Mỗi rank sẽ có bộ nhớ cục bộ và quá trình song song diễn ra ở cấp độ rank.
Một lưu ý thực tế: nếu bạn gán vào một nút duy nhất, hãy để lại 2–4 GB dung lượng trống trên đó. Một nút chạy gần hết dung lượng sẽ kích hoạt quá trình thu hồi, khiến bạn mất đi độ trễ mà bạn đang cố gắng tiết kiệm.
Cài đặt BIOS và kernel cần kiểm tra
Kết quả của công cụ chỉ chính xác như cấu trúc mạng mà firmware hiển thị. Một số cài đặt cần xác nhận:
- Node Interleaving: tắt tính năng này. Khi được bật, BIOS sẽ hiển thị tất cả bộ nhớ dưới dạng một khối phẳng duy nhất và ẩn NUMA khỏi hệ điều hành hoàn toàn.
numactl --hardwaresẽ hiển thị một nút trên hộp đa ổ cắm nếu trường hợp này xảy ra. - Sub-NUMA Clustering (Intel) hoặc Nodes Per Socket (AMD): bật trên các bộ xử lý nhiều nhân khi bạn muốn độ cục bộ cao hơn. Xác nhận
lscpusau khi khởi động lại. vm.zone_reclaim_mode: đặt thành 0 cho hầu hết các máy chủ sản xuất. Giá trị không bằng 0 sẽ thu hồi bộ nhớ cục bộ một cách mạnh mẽ thay vì phân bổ từ xa, điều này có thể loại bỏ bộ đệm trang hữu ích.kernel.numa_balancing: để bật cho các tác vụ chung, tắt khi bạn ghim thủ công. Bộ cân bằng tự động sẽ di chuyển các trang và luồng theo cách xung đột với chính sách của bạn.
Nếu bạn đang chạy tinh chỉnh NUMA trên phần cứng trần, nơi bạn kiểm soát BIOS, các tham số kernel và độ tương thích IRQ, bạn có thể áp dụng tất cả những điều trên mà không cần phải xử lý các trừu tượng hóa của trình ảo hóa. Đó là lý do chính khiến công việc này dễ dàng hơn trên phần cứng chuyên dụng so với trên các máy ảo đám mây.
Đối với các máy chủ chuyên dụng đa socket có quyền truy cập root đầy đủ, hãy tham khảo các máy chủ chuyên dụng của FDC.

Các cấu hình được tối ưu hóa cho việc tối ưu hóa tải công việc trên máy chủ Linux
Cách chọn, áp dụng và tùy chỉnh các cấu hình tối ưu cho máy chủ GPU, cơ sở dữ liệu và máy chủ Linux băng thông cao, kèm theo ví dụ và mẹo triển khai Ansible.
16 phút đọc - 9 tháng 6, 2026
Tối ưu hóa Linux OOM Killer cho VPS: Hướng dẫn thực hành
12 phút đọc - 8 tháng 6, 2026

Bạn có thắc mắc hoặc cần giải pháp tùy chỉnh?
Các tùy chọn linh hoạt
Phạm vi toàn cầu
Triển khai ngay lập tức
Các tùy chọn linh hoạt
Phạm vi toàn cầu
Triển khai ngay lập tức