Cấu Trúc Bảng Lua Hỗ Trợ Khai Triển Lười Biếng Và Cập Nhật Khác Biệt - nói dối e blog

Cấu Trúc Bảng Lua Hỗ Trợ Khai Triển Lười Biếng Và Cập Nhật Khác Biệt

Vài ngày cuối cùng trước Tết Nguyên đán, lần lượt phê duyệt phép năm cho các đồng nghiệp trong nhóm, ai nấy đều trở về quê đón Tết. Tôi ở lại công ty nghiên cứu những thứ không liên quan trực tiếp đến dự án.

Gần đây trên diễn đàn skynet, có cuộc thảo luận sôi nổi về vấn đề cập nhật bảng cấu hình. Qua nhiều năm, dù trong nội bộ công ty hay từ các nguồn bên ngoài, vấn đề này luôn được nhiều người quan tâm. Tôi quyết định tổng kết lại các yêu cầu cốt lõi:

  1. Dữ liệu cung cấp dưới dạng bảng Lua: Hỗ trợ đầy đủ các kiểu dữ liệu gốc như số nguyên, số thực, bảng con, chuỗi, giá trị luận lý. Tùy chọn hỗ trợ hàm và con trỏ.
  2. Bảng dữ liệu thường chứa nhiều (dữ liệu dư thừa) nhưng trong phần lớn trường hợp chỉ sử dụng một phần nhỏ. Quá trình khởi tạo cần nhanh nhẹn tối đa.
  3. Ưu tiên tiết kiệm bộ nhớ. Là dữ liệu bất biến, cần chia sẻ hiệu quả giữa các máy ảo (VM). Ảnh hưởng đến quá trình thu gom rác (GC) phải ở mức tối thiểu.
  4. Cho phép cập nhật dữ liệu mà không cần dừng máy ảo.
  5. Truy cập bảng dữ liệu phải tuân thủ quy ước Lua, hiệu suất gần bằng bảng gốc.

Những yêu cầu này khó có thể thỏa mãn đồng thời, tôi từng đề xuất nhiều giải pháp chỉ đáp ứng từng phần. Trong đó, giải pháp sharetable cho hiệu suất tốt nhất nhờ chỉnh sửa trực tiếp Lua VM, đạt được khả năng chia sẻ dữ liệu giữa các VM. Hiệu suất truy cập dữ liệu ngoài VM bằng với dữ liệu bản địa.

Phương pháp truyền thống như sharedata lưu dữ liệu trong userdata C sẽ có hiệu suất kém hơn. Vì thông qua metatable, mọi thao tác đọc/ghi đều phải đi qua hàm Lua - chậm hơn đáng kể so với bảng Lua gốc.

Lần này tôi thiết kế giải pháp trung gian, dành cho nhu cầu đặc biệt. Nếu lưu toàn bộ dữ liệu trong cấu trúc C sẽ giải quyết bài toán chia sẻ. Để tăng tốc độ truy cập từ Lua, áp dụng kỹ thuật sao chép lười biếng: dữ liệu chưa truy cập giữ nguyên trong C, khi truy cập lần đầu mới sao chép vào VM. Các lần truy cập sau sẽ nhanh như dữ liệu gốc.

Vậy làm thế nào để cập nhật dữ liệu?
Giả sử bảng dữ liệu chỉ hỗ trợ cấu trúc cây (không chu trình, không tái sử dụng nút), key chỉ cho phép chuỗi và số. Khi đó mỗi nút đều có định danh duy nhất - đường dẫn từ gốc đến nút đó.

Đa số bảng cấu hình ít sửa đổi cấu trúc, chủ yếu thay đổi nội dung. Các key thường có giới hạn. Nếu thu thập toàn bộ tên key vào tập hợp và chỉ cho phép thêm mới, mỗi key sẽ có ID số duy nhất. Tương tự, đường dẫn đến nút cũng có mã định danh riêng.

Ví dụ: Nút a.b.c được gán ID 42. Dù sau này bị xóa, ID này vẫn được giữ nguyên. Khi tái tạo lại a.b.c, ID 42 tiếp tục được sử dụng. Nguyên tắc này cho phép cập nhật dữ liệu dễ dàng. Dùng bảng yếu (weak table) ghi nhận các bảng đã khai triển. Khi cập nhật, chỉ cần xóa dữ liệu cũ và phục hồi trạng thái chờ khai triển dựa trên ID duy nhất - hiệu quả hơn nhiều so với cách cập nhật sharetable phải quét toàn bộ VM tìm bảng chia sẻ.

Tôi đã nhanh chóng triển khai ý tưởng này:
Cấu trúc datatree gồm hai phần. Phần chủ (host) là bảng Lua có phương thức update cập nhật dữ liệu. Mọi phiên bản cập nhật đều được lưu lại, nhưng chỉ giữ phiên bản mới nhất.

Phương thức pack đóng gói phiên bản cuối thành userdata C. Đặc biệt, cấu trúc C này nằm trên một khối nhớ liên tục, không chứa con trỏ. Có thể lưu trực tiếp vào file dưới dạng khối dữ liệu bền bỉ, hoặc dùng mmap ánh xạ vào bộ nhớ khi cần.

Trong các VM khác, tạo đối tượng shadow bằng phương thức update nhận địa chỉ userdata C. Hàm này trả về đối tượng Lua có thể khai triển lười biếng. Đối tượng này sử dụng như bảng Lua thông thường. Phương thức update có độ phức tạp O(1), chỉ thực hiện khởi tạo cơ bản. Toàn bộ dữ liệu được sao chép theo nhu cầu khi truy cập. Cấu trúc C được thiết kế cho phép truy cập ngẫu nhiên nhanh, chi phí khai triển mỗi nút chỉ phụ thuộc kích thước dữ liệu của nút đó, không liên quan kích thước gói dữ liệu. Vì update chỉ đọc userdata C, nên hoàn toàn an toàn đa luồng.

Giải pháp này không chỉ phù hợp môi trường máy chủ skynet, mà còn hữu ích cho phía client. Ưu điểm chính là tránh được chi phí khởi tạo khối lượng dữ liệu lớn.

So với sharetable, nhược điểm là dữ liệu không được chia sẻ giữa các VM, gây lãng phí bộ nhớ trong môi trường đa VM. Đồng thời việc sao chép từ C sang Lua tạo ra chi phí ban đầu, nhưng chi phí này được trải đều trong thời gian dài.

0%