Typhon: Khi kiến trúc Game Engine (ECS) giải quyết bài toán Cơ sở dữ liệu

09 tháng 4, 2026·11 phút đọc

Typhon là một engine cơ sở dữ liệu nhúng viết bằng .NET, kết hợp tốc độ của kiến trúc Entity-Component-System (ECS) trong game engine với tính toàn vẹn giao dịch ACID của cơ sở dữ liệu truyền thống. Bằng cách tối ưu hóa locality của bộ nhớ cache và truy cập zero-copy, Typhon mang lại độ trễ dưới micro giây, lý tưởng cho các máy chủ game và mô phỏng thời gian thực.

Typhon: Khi kiến trúc Game Engine (ECS) giải quyết bài toán Cơ sở dữ liệu

Typhon: Khi kiến trúc Game Engine (ECS) giải quyết bài toán Cơ sở dữ liệu

Máy chủ game (game server) luôn đứng ở một giao điểm đầy khó khăn. Chúng cần thông lượng thô (raw throughput) của một game engine — nơi hàng chục nghìn thực thể (entity) được cập nhật mỗi tick — nhưng đồng thời cũng cần những gì cơ sở dữ liệu mang lại: các giao dịch không làm hỏng trạng thái, các truy vấn không cần quét toàn bộ dữ liệu, và khả năng phục hồi sau sự cố.

Hiện nay, các đội phát triển game server thường chọn một phía và cố gắng "vá" phía còn lại. Họ dùng framework Entity-Component-System (ECS) để lấy tốc độ, kết hợp với việc serialize thủ công vào cơ sở dữ liệu để lưu trữ. Hoặc dùng cơ sở dữ liệu để đảm bảo an toàn, nhưng phải chịu sự không tương thích (impedance mismatch) mỗi khi chạm vào trạng thái game.

Typhon ra đời để kết hợp cả hai truyền thống này. Nó là một engine cơ sở dữ liệu lưu trữ dữ liệu theo cách mà game engine làm — và cung cấp các đảm bảo mà máy chủ game cần.

So sánh kiến trúc ECS và Row Store truyền thốngSo sánh kiến trúc ECS và Row Store truyền thống

Sự hội tụ của hai thế giới

Kiến trúc ECS phát triển trong game engine, trong khi cơ sở dữ liệu quan hệ phát triển trong phần mềm doanh nghiệp. Dù ít giao tiếp với nhau, nhưng nhìn vào những gì họ xây dựng, ta thấy sự tương đồng đáng kinh ngạc:

  • Archetype (ECS) tương đương với Table (Database): Lưu trữ đồng nhất, schema cố định.
  • Component (ECS) tương đương với Column (Database): Dữ liệu có kiểu, blittable, có thể lặp hàng loạt.
  • Entity (ECS) tương đương với Row (Database): Danh tính với sự kết hợp động.
  • System (ECS) tương đương với Query (Database): Xử lý tất cả bản ghi khớp với chữ ký.
  • Frame Budget (16ms) tương đương với Latency SLA: Deadline thời gian thực cứng.

Sự hội tụ này cho thấy một sự tổng hợp là khả thi. Nó không phải là ngẫu nhiên — nó được thúc đẩy bởi cùng một quy luật vật lý. Dữ liệu phải được bố trí để phù hợp với CPU cache. Mô hình truy cập phải dự đoán được. Ngân sách độ trễ là có thật.

Bài học từ Game Engine

ECS đã dạy cho thế giới cơ sở dữ liệu những bài học quan trọng về cách lưu trữ dữ liệu. Typhon áp dụng trực tiếp ba bài học này:

1. Cache locality là mặc định

Trong kho lưu trữ hàng (row store) truyền thống, việc đọc vị trí của tất cả người chơi có nghĩa là tải toàn bộ các hàng — tên, kho đồ, máu, v.v. Hầu hết các byte này bị lãng phí. Trong ECS, các thành phần (component) được lưu trữ theo loại: tất cả vị trí liền kề nhau, tất cả giá trị máu liền kề nhau. Việc đọc 10.000 vị trí là một quét bộ nhớ tuyến tính nơi mọi byte đều hữu ích.

Điều này quan trọng hơn nhiều so với suy nghĩ của hầu hết các nhà phát triển. Một lần hit cache L1 tốn khoảng 1 nanosecond. Một lần miss DRAM tốn 60-70 ns — một mức phạt 65 lần. Khi bố trí cơ sở dữ liệu của bạn buộc phải miss cache, không có sự thông minh nào về thuật toán có thể cứu vãn được.

2. Zero-copy là mặc định, không phải tối ưu hóa

Trong cơ sở dữ liệu truyền thống, đọc một bản ghi có nghĩa là deserialize từ trang lưu trữ thành đối tượng ngôn ngữ. Trong ECS, một component đã nằm trong bộ nhớ với bố trí cuối cùng — bạn chỉ cần trả lại một con trỏ. Typhon bảo toàn điều này: các component là các struct unmanaged blittable được đọc trực tiếp từ các trang bộ nhớ được ghim (pinned). Không có serialization, không có cấp phát heap được quản lý, không có sự tham gia của GC.

3. Entity là danh tính thuần túy

Trong ECS, một thực thể chỉ là một ID — một số 64-bit không có cấu trúc vốn có. Tất cả dữ liệu sống bên ngoài trong các bảng component. Đây là ngược lại với tư duy ORM nơi đối tượng là thực thể. Typhon kế thừa điều này: EntityId là một kiểu giá trị nhẹ, tất cả trạng thái sống trong lưu trữ component được kiểu hóa. Sự tách biệt này là điều khiến phần còn lại của kiến trúc trở nên khả thi — phiên bản hóa trên mỗi component, chế độ lưu trữ trên mỗi component, các chỉ mục độc lập cho mỗi loại component.

Bài học từ Cơ sở dữ liệu

Các cơ sở dữ liệu truyền thống đã giải quyết những vấn đề mà ECS chưa bao giờ phải đối mặt. Typhon áp dụng bốn khả năng từ kiến trúc cơ sở dữ liệu:

Giao dịch ACID với MVCC trên mỗi component

Game engine thường không có sự cô lập (isolation). Hai hệ thống sửa đổi cùng một thực thể trong cùng một tick là một điều kiện tranh chấp (race condition) — và trong một game đơn tiến trình, bạn kiểm soát thứ tự thực thi nên có thể quản lý nó. Trên máy chủ game với các phiên chơi đồng thời, bạn không thể làm được điều đó.

Cơ sở dữ liệu đã giải quyết điều này hàng thập kỷ trước với MVCC: cô lập snapshot nơi người đọc không bao giờ chặn người ghi, với phát hiện xung đột tại thời điểm commit. Typhon mang điều này vào — nhưng với một sự thay đổi. Cơ sở dữ liệu truyền thống phiên bản hóa toàn bộ hàng. Typhon phiên bản hóa từng component một cách độc lập. PositionComponent và InventoryComponent của một thực thể duy trì chuỗi sửa đổi riêng của chúng.

Truy vấn linh hoạt

Typhon hỗ trợ cả hai phong cách truy vấn:

  • Truy cập kiểu ECS: tx.GetComponent<Position>(entityId)
  • Truy vấn kiểu Database: tx.Query<Position>().Where(p => p.Level >= 50).OrderByDescending(p => p.Level).Take(10).ExecuteOrdered(tx);

Truy cập component có kiểu của ECS. Lọc vị từ kiểu database với lựa chọn chỉ mục tự động. Bên trong một giao dịch với cô lập snapshot. Trình lập kế hoạch truy vấn chọn quét so với B+Tree dựa trên tính chọn lọc — nhà phát triển không cần phải lo lắng.

Vì máy chủ game có các nhu cầu độ bền (durability) khác nhau cho các hoạt động khác nhau, Typhon cho phép bạn chọn cho mỗi đơn vị công việc:

// Cập nhật vị trí: tốc độ game engine, độ bền theo lô
using var uow = dbe.CreateUnitOfWork(DurabilityMode.Deferred);

// Nhận vật phẩm hiếm: sự an toàn của database, fsync ngay lập tức
using var uow = dbe.CreateUnitOfWork(DurabilityMode.Immediate);

Cùng một engine, cùng một API. Chế độ Deferred mang lại độ trễ commit cấp game engine cho các cập nhật vị trí có thể mô phỏng lại sau sự cố. Chế độ Immediate mang lại đảm bảo cấp database cho một giao dịch trao vật phẩm hiếm có giá trị tiền thật. Máy chủ game quyết định cho mỗi hoạt động — không phải toàn cục.

Chế độ lưu trữ: Không phải dữ liệu nào cũng giống nhau

Máy chủ game không xử lý tất cả dữ liệu giống nhau. Vị trí người chơi thay đổi 60 lần mỗi giây và có thể mô phỏng lại sau sự cố. Các đột biến kho đồ (inventory) hiếm nhưng không bao giờ được mất. Trạng thái runtime AI — mục tiêu hiện tại, điểm mối đe dọa, waypoints tìm đường — được tính toán lại mỗi tick và vô giá trị sau khi khởi động lại.

Typhon cho phép bạn chọn cho mỗi loại component:

ModeLịch sử MVCCLưu trữTheo dõi thay đổiTốt nhất cho
VersionedChuỗi sửa đổi đầy đủCó (WAL + checkpoint)Qua MVCCKho đồ, kinh tế, tiến trình
SingleVersionChỉ trạng thái hiện tạiCó (WAL + checkpoint)DirtyBitmapVị trí, máu, trạng thái cập nhật thường xuyên
TransientChỉ trạng thái hiện tạiKhôngDirtyBitmapBảng đen AI, điểm mối đe dọa, dữ liệu tạm

Các component SingleVersion bỏ qua hoàn toàn chi phí chuỗi sửa đổi — không có bộ đệm vòng, không có cấp phát cho mỗi lần ghi. Chúng theo dõi thay đổi thông qua DirtyBitmap: một bit cho mỗi thực thể, được lật khi ghi, được quét tại rào chắn tick. Đây là cách game engine theo dõi những gì đã thay đổi, và là mô hình đúng cho dữ liệu cập nhật mỗi tick.

Các component Versioned nhận MVCC đầy đủ với cô lập snapshot — người đọc thấy trạng thái lịch sử nhất quán, người ghi không chặn người đọc, xung đột được phát hiện tại thời điểm commit.

Các component Transient không bao giờ chạm đĩa — không WAL, không checkpoint, không phục hồi. Lưu trữ bộ nhớ trong thuần túy với cùng một API truy vấn và lập chỉ mục như mọi thứ khác.

Views: Cầu nối giữa ECS Systems và Database Queries

Trong ECS, một "system" chạy mỗi tick, xử lý tất cả các thực thể khớp. Trong cơ sở dữ liệu, một "materialized view" duy trì một tập kết quả được lưu trong bộ nhớ cache và làm mới nó tăng dần. Views của Typhon là cả hai:

using var view = db.Query()
    .Where(i => i.Rarity >= 3)
    .ToView();

// Game loop
while (running)
{
    using var tx = dbe.CreateQuickTransaction();
    view.Refresh(tx);  // Làm mới tăng dần trong microsecond
    
    // Phản ứng với thay đổi — giống như một ECS system, nhưng chỉ cho những gì thay đổi
    var delta = view.GetDelta();
    foreach (var pk in delta.Added)   SpawnVisual(pk);
    foreach (var pk in delta.Removed) DespawnVisual(pk);
    foreach (var pk in delta.Modified) UpdateVisual(pk);
    
    view.ClearDelta();
}

Lệnh ToView() ban đầu chạy một truy vấn đầy đủ. Sau đó, Refresh() làm rỗng một bộ đệm vòng không khóa (lock-free) của các thay đổi được đẩy bởi đường dẫn commit — chỉ những thực thể có trường được lập chỉ mục thực sự thay đổi mới được đánh giá lại. Nếu 100.000 thực thể khớp với view của bạn nhưng chỉ 12 cái thay đổi kể từ lần làm mới trước, bạn thực hiện 12 đánh giá, không phải 100.000.

Đây là giải pháp cho bài toán "lặp lại mọi thứ" từ phía cơ sở dữ liệu: đừng quét lại, hãy theo dõi delta.

Quy trình truy vấn và Views trong TyphonQuy trình truy vấn và Views trong Typhon

Các đánh đổi (Trade-offs)

Chuyên biệt hóa cho máy chủ game có nghĩa là phải từ bỏ một số thứ:

  • Chỉ component blittable: Không có chuỗi (string), không có tham chiếu đối tượng, không có mảng độ dài biến bên trong component. Văn bản sử dụng các loại kích thước cố định như String64. Đây là cái giá phải trả cho việc đọc zero-copy và lưu trữ thân thiện với cache — và là một ràng buộc mà các nhà phát triển game đã quen thuộc từ các framework ECS.
  • Mối quan hệ tập trung vào thực thể, không phải SQL JOINs: Typhon hỗ trợ các liên kết điều hướng, quan hệ 1:N và N:M — nhưng chúng tuân theo tham chiếu thực thể, gần hơn với cơ sở dữ liệu đồ thị (graph database) hơn là SQL truyền thống. Điều này phù hợp với cách máy chủ game tự nhiên suy nghĩ về dữ liệu, nhưng nếu mô hình tinh thần của bạn là SELECT ... FROM a JOIN b, đây là một mô hình khác.
  • Schema trong code, không phải SQL: Các component là các struct C# với thuộc tính, không phải câu lệnh DDL. Tự nhiên cho nhà phát triển game, nhưng lạ lẫm với quản trị viên cơ sở dữ liệu.

Typhon đại diện cho một bước tiến thú vị, áp dụng các nguyên tắc thiết kế hướng dữ liệu (Data-Oriented Design) từ thế giới game vào kiến trúc cơ sở dữ liệu, tạo ra một công cụ mạnh mẽ dành riêng cho các ứng dụng thời gian thực đòi hỏi hiệu suất cao nhất.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗