Cấu hình File System tối ưu cho Seastar
Seastar hoạt động dựa trên kiến trúc zero-copy và direct I/O, do đó file system truyền thống như ext4 với journaling mặc định sẽ gây ra bottleneck. Chúng ta cần cấu hình XFS hoặc ext4 với các tham số đặc biệt để giảm thiểu overhead của kernel.
1. Chuẩn bị và định dạng ổ đĩa với XFS
Sử dụng XFS vì nó hỗ trợ tốt hơn cho các workload có độ trễ thấp và throughput cao so với ext4 trong môi trường Seastar. Bước này bao gồm việc định dạng ổ đĩa target (ví dụ: /dev/sdb) với các tham số tối ưu.
Tham số quan trọng: logbufs (tăng bộ đệm journal), logbsize (kích thước journal block), và nojournal nếu bạn chấp nhận rủi ro mất dữ liệu để đổi lấy performance cực đại (chỉ dùng cho test).
mkfs.xfs -f -d agcount=4 -i size=4096 -b size=4096 -l size=128m -E lazy-count=1 /dev/sdb
Kết quả mong đợi: Ổ đĩa /dev/sdb được định dạng thành XFS với 4 allocation groups, inode size 4096 byte, block size 4096 byte và log buffer 128MB.
2. Mount với tham số tối ưu hóa I/O
Khi mount, chúng ta cần tắt các cơ chế làm chậm I/O như barrier (nếu hardware hỗ trợ), tắt atime (không cập nhật thời gian truy cập), và sử dụng noatime để giảm write overhead.
Tạo thư mục mount point:
mkdir -p /mnt/seastar_disk
Thực hiện mount với các flag quan trọng: noatime, nodiratime, inode64, và logbufs (nếu cần thiết). Lưu ý: Seastar thường yêu cầu direct I/O nên barrier có thể gây delay nếu không được cấu hình đúng.
mount -o noatime,nodiratime,inode64,allocsize=128m /dev/sdb /mnt/seastar_disk
Kết quả mong đợi: Hệ thống hiển thị thông báo mount thành công. Kiểm tra bằng lệnh mount | grep seastar_disk để xác nhận các flag đã được áp dụng.
3. Cấu hình ext4 (Fallback nếu không dùng XFS)
Nếu bắt buộc sử dụng ext4, cần tắt journaling data hoặc chuyển sang mode writeback để giảm độ trễ, đồng thời tăng kích thước block.
mkfs.ext4 -F -b 4096 -O ^has_journal /dev/sdc
Mount với các tùy chọn tương tự:
mount -o noatime,nodiratime,data=writeback /dev/sdc /mnt/seastar_disk_ext4
Kết quả mong đợi: ext4 được định dạng không có journal data, giảm overhead ghi nhật ký, mount với chế độ writeback để bypass journaling cho dữ liệu.
Tinh chỉnh Direct I/O và O_DIRECT
Seastar sử dụng O_DIRECT để bypass page cache của Linux, gửi dữ liệu trực tiếp từ user space xuống disk driver. Điều này yêu cầu alignment (căn chỉnh) chính xác giữa kích thước buffer và sector size của disk.
1. Kiểm tra Sector Size và Alignment
Trước khi chạy ứng dụng, phải xác định sector size vật lý của disk để đảm bảo buffer của Seastar được căn chỉnh đúng. Nếu sai lệch, hiệu năng sẽ giảm mạnh do kernel phải copy dữ liệu.
blockdev --getss /dev/sdb
Kết quả mong đợi: Trả về giá trị sector size, thường là 512 hoặc 4096 byte. Nếu là 4096 (Advanced Format), buffer của Seastar phải là bội số của 4096.
2. Cấu hình Seastar để sử dụng O_DIRECT
Trong code C++ của Seastar (hoặc khi chạy binary), cần bật flag --io-properties hoặc cấu hình io_properties trong file cấu hình để enforce O_DIRECT.
Tạo file cấu hình I/O properties tại /etc/seastar/io.properties:
cat > /etc/seastar/io.properties
Kết quả mong đợi: File /etc/seastar/io.properties được tạo với nội dung chính xác. Khi khởi động Seastar, nó sẽ đọc file này và tự động áp dụng O_DIRECT.
3. Verify O_DIRECT hoạt động
Sử dụng công cụ strace hoặc ftrace để xác nhận hệ thống gọi system call open với flag O_DIRECT (0x80000).
strace -e trace=openat -p 2>&1 | grep O_DIRECT
Kết quả mong đợi: Dòng output hiển thị openat(..., flags=O_RDWR|O_DIRECT). Nếu không thấy O_DIRECT, Seastar đang fallback sang page cache, cần kiểm tra lại alignment.
Tinh chỉnh Log Segments và Block Size
Trong kiến trúc Log-Structured, dữ liệu được ghi thành các đoạn (segments). Kích thước của segment và block ảnh hưởng trực tiếp đến throughput và độ trễ (latency) khi compact (compaction).
1. Xác định kích thước Segment tối ưu
Segment quá nhỏ gây ra quá nhiều metadata overhead và I/O random. Segment quá lớn làm tăng thời gian recovery sau crash và giảm hiệu quả compaction. Đối với SSD NVMe, kích thước 256MB - 1GB là phổ biến.
Cấu hình trong code Seastar (ví dụ trong class log_segment hoặc config file):
cat > /etc/seastar/segment_config.json
Kết quả mong đợi: File JSON được tạo. Khi khởi động DB, Seastar sẽ đọc giá trị này để phân bổ không gian log. Với 256MB, mỗi write operation sẽ tập trung vào một vùng lớn, tận dụng write amplification thấp.
2. Điều chỉnh Block Size cho Write Coalescing
Seastar sử dụng cơ chế coalescing để gộp nhiều write nhỏ thành một write lớn. Kích thước block phải tương thích với stripe width của RAID hoặc sector size của disk.
Tham số --block-size khi chạy command line:
./seastar_db --block-size 4096 --io-page-size 4096 --io-batch-size 128
Kết quả mong đợi: Seastar khởi động với block size 4K. Điều này đảm bảo mỗi I/O request là một multiple của sector size, tối ưu hóa cho O_DIRECT.
3. Verify hiệu năng sau tinh chỉnh
Sử dụng fio để benchmark với cùng tham số đã cấu hình, so sánh trước và sau khi tinh chỉnh.
fio --name=direct_test --ioengine=libaio --iodepth=32 --rw=write --bs=4k --direct=1 --size=10G --numjobs=4 --runtime=60 --time_based --group_reporting --filename=/mnt/seastar_disk/testfile
Kết quả mong đợi: Output hiển thị IOPS cao và Latency (lat_ns) ổn định. Nếu latency dao động mạnh, có thể do segment size quá nhỏ hoặc compaction chạy liên tục.
Cấu hình CPU Affinity và Thread Pinning
Seastar sử dụng model Sharded với mỗi shard chạy trên một core CPU vật lý riêng biệt. Việc pin (ghim) thread vào core cụ thể là bắt buộc để tránh context switch và tận dụng cache L3.
1. Kiểm tra Topology CPU
Xác định số lượng core vật lý (physical cores) và socket để tránh pin các thread vào cùng một socket nếu có NUMA, hoặc đảm bảo mỗi thread nằm trên một core riêng biệt.
lscpu | grep "Core(s)"
numactl --hardware
Kết quả mong đợi: Biết được số core vật lý (ví dụ: 32 cores). Nếu có NUMA nodes, cần pin thread vào node RAM gần nhất.
2. Cấu hình Affinity khi khởi động Seastar
Seastar hỗ trợ các flag để tự động pin thread hoặc chỉ định thủ công. Sử dụng --io-cpu để chỉ định CPU cho I/O và --cpu-set để giới hạn các core cho application.
Kịch bản: Chạy 4 shard, mỗi shard pin vào core 0, 1, 2, 3.
./seastar_db --cpu-set 0-3 --io-cpu 4-7 --smp 4
Giải thích:
- --smp 4: Chạy 4 shard (4 thread chính).
- --cpu-set 0-3: Pin 4 thread này vào core 0, 1, 2, 3.
- --io-cpu 4-7: Dành riêng core 4, 5, 6, 7 cho I/O reactor của Seastar (tách biệt với logic business).
Kết quả mong đợi: Seastar khởi động và log hiển thị "Starting 4 shards on cores 0,1,2,3".
3. Sử dụng taskset để pin thủ công (Fallback)
Nếu Seastar không tự pin đúng, sử dụng taskset để ép buộc process vào core trước khi chạy.
taskset -c 0-7 ./seastar_db --smp 4 --io-cpu 4-7
Kết quả mong đợi: Process Seastar chỉ chạy trên core 0-7. Kiểm tra bằng lệnh ps -eLo pid,tid,class,pri,psr,pcpu,cmd | grep seastar để thấy cột PSR (Processor) khớp với core đã chỉ định.
4. Verify Affinity và Load
Sử dụng htop hoặc mpstat để kiểm tra CPU load. Nếu affinity đúng, load sẽ phân bổ đều trên các core đã pin, và các core khác sẽ gần như 0%.
mpstat -P ALL 1
Kết quả mong đợi: Các core được pin (ví dụ 0-7) có %usr hoặc %sys cao, các core còn lại (8-31) gần như 0%. Điều này chứng tỏ thread pinning thành công, tránh nhiễu từ các process khác.
Điều hướng series:
Mục lục: Series: Triển khai Database Log-Structured với Seastar trên Ubuntu 24.04
« Phần 4: Triển khai database Log-Structured đơn giản với Seastar
Phần 6: Xử lý sự cố, giám sát và các tips nâng cao cho production »