Cấu hình Hugepages để giảm Overhead TLB
Hugepages (Trang bộ nhớ lớn) cho phép hệ điều hành phân bổ bộ nhớ RAM theo đơn vị 2MB hoặc 1GB thay vì 4KB mặc định. Việc này giảm đáng kể số lượng mục trong bảng TLB (Translation Lookaside Buffer), giúp Vector Database truy cập RAM nhanh hơn khi xử lý các index lớn.
Bước này cần thực hiện trước khi khởi động dịch vụ Database để đảm bảo Hugepages được phân bổ sẵn sàng.
1. Kiểm tra và tính toán dung lượng Hugepages
Trước khi cấu hình, cần xác định tổng RAM và chọn kích thước trang phù hợp. Với Ubuntu 24.04, mặc định thường hỗ trợ 2MB, nhưng với RAM lớn (>64GB), nên ưu tiên 1GB để tối ưu TLB.
Thực hiện lệnh sau để xem cấu hình hiện tại:
grep Huge /proc/meminfo
Kết quả mong đợi: Bạn sẽ thấy các dòng như HugePages_Total, HugePages_Free. Nếu số này là 0, hệ thống chưa cấu hình Hugepages.
2. Cấu hình Hugepages vĩnh viễn qua systemd
Tạo file cấu hình systemd để tự động phân bổ Hugepages khi máy khởi động. Cách này an toàn và linh hoạt hơn việc chỉnh sửa grub.conf.
Tạo file /etc/systemd/system/hugepages.conf với nội dung sau (giả sử bạn có 64GB RAM và muốn dùng 16GB cho Hugepages 2MB):
cat > /etc/systemd/system/hugepages.conf
Giải thích: 8192 trang 2MB = 16GB, 16 trang 1GB = 16GB. Điều chỉnh con số này tùy theo RAM thực tế của server.
Kết quả mong đợi: File được tạo thành công, không báo lỗi cú pháp.
3. Kích hoạt và verify Hugepages
Khởi động lại dịch vụ Hugepages và kiểm tra lại trạng thái bộ nhớ.
systemctl daemon-reload
systemctl start hugepages
systemctl enable hugepages
grep Huge /proc/meminfo
Kết quả mong đợi: Dòng HugePages_Total sẽ tăng lên bằng tổng số trang bạn đã cấu hình (ví dụ: 8208 nếu dùng cả 2MB và 1GB), và HugePages_Free cũng tương tự.
Tối ưu hóa NUMA cho kiến trúc Đa Socket
Trên các server có nhiều CPU (Multi-socket), kiến trúc NUMA (Non-Uniform Memory Access) gây ra độ trễ nếu core CPU truy cập RAM của socket khác. Vector Database cần dữ liệu nằm gần CPU xử lý để đạt hiệu năng tối đa.
1. Kiểm tra cấu trúc NUMA
Xác định số lượng node NUMA và cách phân bổ core/RAM.
numactl --hardware
Kết quả mong đợi: Bạn sẽ thấy danh sách các node (Node 0, Node 1...) kèm theo số core và dung lượng RAM gắn liền với từng node.
2. Cấu hình chính sách NUMA mặc định
Đổi chính sách NUMA từ interleave (xích ngang) sang local để ưu tiên truy cập bộ nhớ cục bộ, giảm latency.
Sửa file /etc/default/grub để thêm tham số kernel:
sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="numa=local /' /etc/default/grub
Tại sao: Tham số numa=local buộc kernel phân bổ bộ nhớ cho tiến trình trên node NUMA gần nhất thay vì phân tán đều.
Kết quả mong đợi: File grub được cập nhật. Cần reboot để (có hiệu lực).
3. Chạy Database với ràng buộc NUMA
Thay vì chạy Database trên toàn bộ máy, hãy giới hạn nó vào một NUMA node cụ thể (ví dụ Node 0) để tránh contention.
Sử dụng lệnh numactl khi khởi động Weaviate hoặc Milvus. Ví dụ với Weaviate:
numactl --cpunodebind=0 --membind=0 /usr/bin/weaviate-server
Kết quả mong đợi: Database chỉ chạy trên các core và RAM của Node 0. Kiểm tra bằng lệnh ps -eo pid,command hoặc taskset -cp để xác nhận core bị giới hạn.
Áp dụng CPU Pinning để giảm Contention
CPU Pinning (Ghép nối CPU) gán cố định các luồng (thread) của Database vào các nhân vật lý cụ thể. Điều này giảm hiện tượng migration (di chuyển thread giữa các core), giảm cache miss và tăng tính ổn định của độ trễ (latency).
1. Xác định các Core vật lý (Physical Cores)
Tránh gán các core ảo (Hyper-threading/SMT) cho cùng một luồng quan trọng để giảm tranh chấp tài nguyên cache L1/L2.
lscpu --extended | grep -E "Model|Core|Socket"
Kết quả mong đợi: Danh sách các core. Chọn các core có số lẻ hoặc chẵn tùy thuộc vào chiến lược (ví dụ: chỉ dùng core 0, 8, 16... nếu đó là core chính trong mỗi socket).
2. Cấu hình CPU Affinity cho dịch vụ
Sử dụng taskset để ghép tiến trình vào các core đã chọn. Giả sử bạn chọn 8 core vật lý (0-7).
taskset -p -c 0-7
Để cấu hình vĩnh viễn, sửa file systemd unit của Database (ví dụ /etc/systemd/system/weaviate.service):
cat >> /etc/systemd/system/weaviate.service
Giải thích: Tham số CPUAffinity trong systemd sẽ tự động áp dụng taskset khi dịch vụ khởi động.
Kết quả mong đợi: Sau khi chạy systemctl restart weaviate, tiến trình sẽ chỉ chạy trên core 0-7.
3. Verify CPU Pinning
Kiểm tra lại xem tiến trình có đang chạy đúng core đã ghép không.
ps -eLo pid,psr,comm | grep weaviate
Kết quả mong đợi: Cột PSR (Processor) chỉ hiển thị các số từ 0 đến 7, không có số nào khác.
Cấu hình IRQ Balancing cho Network và Disk
IRQ (Interrupt Request) là tín hiệu phần cứng gửi đến CPU. Nếu các interrupt từ Card mạng (NIC) hoặc Disk Controller bị xử lý bởi các core đang chạy Database, sẽ gây ra jitter (độ trễ không ổn định). Cần ghép IRQ vào các core "nông" (non-database) hoặc cân bằng đều.
1. Xác định IRQ cho thiết bị
Tìm số IRQ của Card mạng và Disk Controller.
cat /proc/interrupts | grep -E "eth0|sda|nvme|virtio"
Kết quả mong đợi: Danh sách các dòng hiển thị số IRQ (ví dụ: IRQ 52, IRQ 68...) tương ứng với thiết bị.
2. Ghép IRQ vào Core riêng biệt
Giả sử bạn đã dành các core 0-7 cho Database. Hãy ghép IRQ vào core 8 trở đi để tránh xung đột.
Ví dụ: Ghép IRQ 52 vào core 8.
echo 256 > /proc/irq/52/smp_affinity_list
Giải thích: Giá trị trong smp_affinity_list là danh sách core. Số 256 tương ứng với core 8 (vì 2^8 = 256). Nếu muốn ghép vào nhiều core (8-15), dùng chuỗi 8-15.
Để làm điều này vĩnh viễn, tạo file /etc/irqbalance.conf hoặc script khởi động. Cách đơn giản nhất là dùng irqbalance daemon đã được cài sẵn trên Ubuntu.
systemctl enable irqbalance
systemctl start irqbalance
Kết quả mong đợi: Dịch vụ irqbalance chạy và tự động di chuyển IRQ đến các core ít tải. Kiểm tra bằng cat /proc/interrupts sau vài giây, bạn sẽ thấy số lần interrupt tăng trên các core 8+.
3. Tắt IRQ Balance trên Core Database (Tùy chọn nâng cao)
Nếu bạn muốn chắc chắn 100% Database không bị gián đoạn bởi IRQ, hãy loại trừ các core Database khỏi cân bằng IRQ.
echo "0-7" > /proc/sys/kernel/irqbalance_cpu_list
Lưu ý: Cách này chỉ có tác dụng nếu irqbalance hỗ trợ tham số này trong phiên bản Ubuntu 24.04 (thường hỗ trợ qua file cấu hình /etc/irqbalance.conf với tham số IRQBALANCE_BANNED_CPUS=ff cần tính toán bitmask).
Cấu hình an toàn nhất qua file /etc/irqbalance.conf:
cat > /etc/irqbalance.conf
Giải thích: ff là bitmask (11111111) tương ứng với 8 core đầu tiên (0-7) bị cấm không nhận IRQ.
Kết quả mong đợi: Sau khi systemctl restart irqbalance, các core 0-7 sẽ không còn nhận bất kỳ interrupt nào từ phần cứng, chỉ core 8+ mới xử lý.
Điều hướng series:
Mục lục: Series: Tối ưu hóa Database Vector trên Ubuntu 24.04 với Linux Kernel Tuning
« Phần 3: Cài đặt và cấu hình Vector Database (Weaviate/Milvus)
Phần 5: Tối ưu hóa lưu trữ: Filesystem, Disk Cache và Write-back »