Tại sao tôi lại xây dựng một Database Engine bằng C#: Đánh vỡ định kiến về hiệu năng
Bài viết này chia sẻ hành trình xây dựng Typhon, một cơ sở dữ liệu nhúng ACID hiệu năng cao bằng ngôn ngữ C#. Tác giả chứng minh rằng với các tính năng hệ thống hiện đại như unsafe code và ref struct, C# hoàn toàn có thể đạt được độ trễ ở mức microsecond, tương đương với C++ hay Rust.

Tại sao tôi lại xây dựng một Database Engine bằng C#: Đánh vỡ định kiến về hiệu năng
Khi tôi nói với mọi người rằng mình đang xây dựng một cơ sở dữ liệu (database engine) tuân thủ chuẩn ACID bằng C#, phản ứng đầu tiên tôi nhận được luôn giống nhau: "Nhưng còn GC pauses (độ trễ do bộ thu gom rác) thì sao?"
Đây là một câu hỏi hoàn toàn hợp lý. Rất ít người chọn xây dựng các cơ sở dữ liệu hiệu năng cao trên nền tảng .NET. Định kiến phổ biến là bạn cần C, C++ hoặc Rust cho loại phần mềm này — rằng các ngôn ngữ được quản lý (managed languages) về cơ bản không đủ khả năng để gia nhập "câu lạc bộ độ trễ vi giây" (microsecond-latency club).
Tuy nhiên, sau 30 năm xây dựng các engine 3D thời gian thực và phần mềm hệ thống, tôi vẫn chọn C#. Dự án này có tên là Typhon: một engine cơ sở dữ liệu nhúng ACID nhắm đến mục tiêu cam kết giao dịch trong 1–2 microgiây. Những lý do đằng sau sự lựa chọn này có thể sẽ thay đổi cách bạn nhìn nhận về khả năng của C#.
Những lập luận chống lại C#
Trước khi đi vào lý do tại sao C# lại phù hợp, hãy thẳng thắn nhìn nhận những lo ngại lớn nhất khi chọn ngôn ngữ này cho các hệ thống cấp thấp:
- GC (Garbage Collector) là không xác định: Nó có thể tạm dừng tất cả các luồng (threads) bất cứ lúc nào. Đối với một engine database hứa hẹn độ trễ ở mức microgiây, một lần dọn dẹp Gen2 kéo dài 10ms là thảm họa — gấp 10.000 lần ngân sách độ trễ cho phép.
- Bạn không kiểm soát bố cục bộ nhớ: Managed heap quyết định vị trí của các đối tượng. GC có thể di chuyển chúng xung quanh trong quá trình nén bộ nhớ. Bạn không thể đảm bảo các nút B+Tree của mình nằm trên ranh giới cache-line, hay bộ đệm page cache không bị di chuyển giữa chừng.
- JIT warmup: Lần gọi đầu tiên đến bất kỳ phương thức nào đều phải trả chi phí biên dịch. Trong database engine, giao dịch đầu tiên sau khi khởi động không nên chậm hơn 100 lần so với trạng thái ổn định.
- Chi phí ảo: Mọi truy cập mảng đều có kiểm tra giới hạn ẩn. Mọi gọi interface đều đi qua bảng ảo (vtable). Trong các vòng lặp nóng xử lý hàng triệu thực thể, những nano-giây này sẽ cộng lại thành vấn đề lớn.
Đây đều là những vấn đề thực tế. Nhưng điều mà hầu hết mọi người bỏ qua là: C# hiện đại có câu trả lời cho từng vấn đề một.
Khía cạnh hệ thống của C# mà ít người biết đến
C# mà hầu hết các nhà phát triển biết — các lớp (classes), garbage collection, LINQ — chỉ là một nửa của ngôn ngữ. Có một khía cạnh hoàn toàn khác mà đội ngũ .NET runtime đã âm thầm xây dựng trong một thập kỷ, và nó trông không giống như bạn tưởng tượng chút nào.
unsafemang lại kiểm soát cấp C: Con trỏ thô (raw pointers), số học con trỏ,stackalloccho bộ đệm ngăn xếp, mảng kích thước cố định — JIT tạo ra các lệnh mov/cmp/jne giống hệt như bạn nhận được từ C. Không phải "gần giống C", mà là cùng một lệnh.GCHandle.Alloc(Pinned)vô hiệu hóa GC nơi cần thiết: Bạn có thể ghim (pin) các mảng byte để GC không bao giờ di chuyển chúng. Toàn bộ page cache của Typhon là bộ nhớ được ghim — GC không chạm vào, không quét, không di chuyển. Nó chỉ là các byte thô tại một địa chỉ cố định, giống hệtmalloctrong C.ref structloại bỏ cấp phát bộ nhớ trên heap: Mộtref structkhông thể thoát ra heap. Nó sống trên stack, chết khi phạm vi kết thúc, và GC không bao giờ biết nó tồn tại. Entity accessor của Typhon là mộtref struct96 byte — không cấp phát, không áp lực lên GC.- Generics bị giới hạn mang lại đơn hình hóa (monomorphization) thực sự: Khi bạn viết
where T : unmanaged, JIT tạo ra một đường dẫn mã gốc riêng cho từng tham số kiểu.sizeof(T)trở thành hằng số. Các nhánh chết bị loại bỏ. - Hardware intrinsics là hạng nhất:
System.Runtime.Intrinsicscung cấpVector256,Sse42.Crc32,BitOperations.TrailingZeroCount— cùng các lệnh SIMD có sẵn trong C/C++, với cùng hiệu năng và khả năng phát hiện tính năng phần cứng tại thời gian chạy.
Kiến trúc của Typhon
Typhon trông như thế nào trong thực tế
Lý thuyết thì hay, nhưng hãy xem mã thực tế.
Tổng kiểm tra WAL tăng tốc phần cứng Mỗi trang được ghi vào Write-Ahead Log đều cần tổng kiểm tra CRC32C. Trong C#, bạn có thể gọi các lệnh CPU theo tên trực tiếp:
private static uint ComputePartial(uint crc, ReadOnlySpan<byte> data)
{
if (Sse42.X64.IsSupported) return ComputeSse42X64(crc, data);
if (Sse42.IsSupported) return ComputeSse42X32(crc, data);
if (ArmCrc32.Arm64.IsSupported) return ComputeArm64(crc, data);
return ComputeSoftware(crc, data);
}
Đây không phải là "P/Invoke chậm". Đây là các lệnh nội tại (intrinsics) được biên dịch trực tiếp thành các lệnh máy crc32q hoặc crc32w.
Truy cập dữ liệu Zero-Copy
Typhon sử dụng ref struct để trả về tham chiếu trực tiếp đến bộ nhớ thô được ghim:
public ref T Read<T>(Comp comp) where T : unmanaged
{
// ... logic tìm vị trí ...
return ref _tx.ReadEcsComponentData(table, chunkId);
}
Lời gọi Read này đi từ lời gọi phương thức → tra cứu slot → ID chunk → page cache → số học con trỏ → ref readonly T trỏ trực tiếp vào trang bộ nhớ được ghim. Không sao chép. Không cấp phát. Không có sự tham gia của GC.
Lợi thế về năng suất
Một engine cơ sở dữ liệu không chỉ là các đường dẫn nóng (hot paths). Xung quanh lõi engine là một lớp vỏ lớn của cơ sở hạ tầng: quản lý cấu hình, ghi log có cấu trúc, telemetry, dependency injection, kiểm thử, benchmarking.
Trong C hoặc Rust, bạn sẽ phải tự xây dựng phần lớn những thứ này hoặc kết nối các thư viện với chất lượng không đồng đều. Trong .NET, mọi thứ đều có sẵn và miễn phí: ILogger và OpenTelemetry để quan sát, BenchmarkDotNet cho micro-benchmark nghiêm ngặt, NUnit để kiểm thử.
Đối với một nhà phát triển độc lập xây dựng engine cơ sở dữ liệu, đây là lợi thế cạnh tranh thực sự. Tôi dành thời gian cho các nguyên thủy đồng thời và loại bỏ page cache, thay vì phát minh lại một framework ghi log.
Quan trọng là bố cục bộ nhớ, không phải ngôn ngữ
Bài học lớn từ nhiều năm xây dựng engine 3D thời gian thực là: điểm nghẽn trong engine cơ sở dữ liệu là mô hình truy cập bộ nhớ, không phải thông lượng lệnh.
Một lần cache miss đến DRAM trên Ryzen 7950X tốn 61–73 nanogiây. Đó là ~250 chu kỳ CPU ngồi không chờ dữ liệu. Một thao tác CAS trúng L1 chỉ tốn 1.4 nanogiây. Tỷ lệ là 50:1.
Không có bao nhiêu "trừu tượng zero-cost" trong ngôn ngữ của bạn có thể cứu bạn nếu cấu trúc dữ liệu gây ra cache miss. Ngược lại, nếu bố cục dữ liệu thân thiện với cache — liền kề, căn chỉnh, mô hình truy cập có thể dự đoán — ngôn ngữ hầu như không quan trọng. C# với unsafe tạo ra mã máy giống hệt C trên các đường dẫn nóng.
Con số biết nói
Tất cả các phép đo trên Ryzen 9 7950X, .NET 10.0, BenchmarkDotNet:
- Vòng đời CRUD MVCC: 1.2 µs (830K ops/sec)
- B+Tree lookup (hit): 267 ns (3.7M ops/sec)
- Quét tuần tự B+Tree: 2.1 ns (479M keys/sec)
- Lock acquire (không tranh chấp): 7.8 ns
Đây là các số liệu phiên bản alpha sớm. Nhưng chúng xác nhận luận điểm cốt lõi: C# không phải là điểm nghẽn.
Sự đánh đổi
Không có sự lựa chọn nào là miễn phí. An toàn bộ nhớ là trách nhiệm của bạn. Trong các khối unsafe, bạn có thể làm hỏng bộ nhớ, hủy tham chiếu con trỏ sai... trình biên dịch sẽ không cứu bạn.
Tuy nhiên, C# có một mẹo mà Rust không có: Roslyn analyzers. Tôi đã viết một bộ analyzer tùy chỉnh thực thi các quy tắc an toàn cụ thể theo miền dưới dạng lỗi biên dịch, giúp theo dõi quyền sở hữu và xử lý tài nguyên một cách nghiêm ngặt.
Bạn không nhận được sự an toàn của Rust miễn phí trong C#. Nhưng bạn có thể xây dựng tập hợp con chính xác những gì bạn cần dưới dạng lỗi biên dịch, được tùy chỉnh cho miền của bạn.
Kết luận
Trong bài viết tiếp theo, tôi sẽ giải thích tại sao một engine cơ sở dữ liệu ACID lại vay mượn kiến trúc lưu trữ từ các engine game — cụ thể là mô hình Entity-Component-System. Engine game và cơ sở dữ liệu đang giải quyết cùng một vấn đề cơ bản: quản lý dữ liệu có cấu trúc với các ràng buộc hiệu năng cực đoan.
Họ chỉ phát triển các giải pháp hoàn toàn khác nhau. Nhưng với Typhon, tôi đang chứng minh rằng ranh giới đó đang mờ dần.
Bài viết liên quan

Phần mềm
Anthropic ra mắt Claude Opus 4.7: Nâng cấp mạnh mẽ cho lập trình nhưng vẫn thua Mythos Preview
16 tháng 4, 2026

Công nghệ
Qwen3.6-35B-A3B: Quyền năng Lập trình Agentic, Nay Đã Mở Cửa Cho Tất Cả
16 tháng 4, 2026

Công nghệ
Spotify thắng kiện 322 triệu USD từ nhóm pirate Anna's Archive nhưng đối mặt với bài toán thu hồi
16 tháng 4, 2026
