Ghi Chú Phát Triển (20): Hệ Thống Giao Dịch - nói dối e blog

Ghi Chú Phát Triển (20): Hệ Thống Giao Dịch

Hệ thống giao dịch và rơi đồ không nằm trong mục tiêu giai đoạn đầu, nhưng vì hiện tại chưa có nhiều việc gấp, tôi quyết định bắt tay vào thiết kế trước.

Về bản chất, rơi đồ và giao dịch là hai mặt của một vấn đề - đều là quá trình chuyển giao tài sản giữa người chơi với nhau hoặc giữa người chơi và hệ thống. Năm ngoái tôi đã từng viết một bài blog phân tích chi tiết về chủ đề này, nhưng lần thiết kế lại này tôi có điều chỉnh nhẹ để đơn giản hóa. Nguyên tắc thiết kế vẫn là: máy chủ giao dịch độc lập, gọn nhẹ, ổn định và dễ truy vết lịch sử giao dịch để phục hồi dữ liệu.

Theo quan điểm của tôi, hệ thống giao dịch đóng vai trò như “người canh giữ cuối cùng”. Khi mọi thứ vận hành bình thường, nó gần như ẩn mình, chỉ can thiệp khi cần đảm bảo không xảy ra hiện tượng nhân bản tài sản và mỗi vật phẩm luôn có chủ sở hữu duy nhất. Trong các tranh chấp, nó đóng vai trò trọng tài.

Trong hệ thống trò chơi, vật phẩm của người chơi hoặc thực thể khác sẽ được lưu trữ trong dữ liệu người chơi thông qua cơ chế bộ nhớ chia sẻ. Mỗi vật phẩm được ghi nhận trong ô hành trang tương ứng, lưu trữ đồng thời ID đại diện cho loại vật phẩm và một (hoặc nhiều) ID duy nhất cho từng vật phẩm cụ thể (dùng 64bit hoặc 128bit). Cách tiếp cận này giúp tối ưu hiệu suất khi truy cập chức năng vật phẩm, tránh phải tra cứu bảng dữ liệu phụ.

Dịch vụ xác thực giao dịch chỉ tập trung vào một việc duy nhất: xác định chủ sở hữu của từng ID vật phẩm. Nó không quan tâm đến ý nghĩa của ID đó. Tất nhiên, sẽ có cơ sở dữ liệu riêng để ánh xạ giữa ID và thông tin vật phẩm. Khi chủ sở hữu thay đổi, hệ thống giao dịch đảm bảo tính nguyên tử của giao dịch. Không chỉ xử lý giao dịch giữa người chơi, mà còn quản lý lượng lớn giao dịch với hệ thống - ví dụ như rơi đồ (chuyển từ hệ thống sang người chơi) hay hủy vật phẩm (chuyển từ người chơi về hệ thống).

Tôi phân loại đối tượng trong hệ thống giao dịch thành ba nhóm:

  • Entity: Đại diện cho chủ sở hữu
  • Goods: Vật phẩm có ID duy nhất
  • Currency: Tiền tệ, chỉ cần ghi nhận loại tiền và số lượng (không cần ID duy nhất)

Mỗi giao dịch bao gồm nhiều thành phần, mỗi thành phần có thể là GoodsID hoặc cặp CurrencyType + CurrencyAmount. Để kiểm tra tính hợp lệ, hệ thống yêu cầu ràng buộc giữa chủ sở hữu và số dư tài khoản. Thông tin cần thiết cho mỗi bên trong giao dịch gồm:
EntityID (ID chủ sở hữu) | Balances (số dư tiền tệ) | {GoodsID (ID vật phẩm) | Currency (số tiền giao dịch)}

Hiện tại tôi chỉ hỗ trợ giao dịch hai bên, nghĩa là một yêu cầu giao dịch chỉ cần cung cấp thông tin từ hai thực thể là có thể hoàn thành trao đổi.

Hệ thống sẽ kiểm tra:

  1. Số dư tài khoản hai bên có chính xác không
  2. Chủ sở hữu của các vật phẩm giao dịch có đúng không
    Nếu phát hiện sai sót, giao dịch sẽ bị hủy và trả về lỗi cụ thể (ví dụ: vật phẩm không hợp lệ hoặc số dư không khớp).

Các giao thức khác giữ nguyên như bài viết trước, chỉ bổ sung thêm tham số khởi tạo số dư tài khoản khi tạo Entity để tiện lợi hơn. Vật phẩm khởi tạo cũng có thể thiết lập chủ sở hữu ban đầu thay vì mặc định thuộc về hệ thống rồi mới chuyển giao.

Hệ thống không giới hạn ở một ID duy nhất. Trong cơ chế rơi đồ, mỗi bản đồ hay phó bản đều có EntityID riêng. Khi bắt đầu phó bản, hệ thống sẽ khởi tạo sẵn các vật phẩm có thể rơi ra (với bản đồ mở, cơ chế phức tạp hơn một chút, cần tính đến yếu tố thời gian và số lượng người chơi). Tiền tệ cũng được phân bổ định mức vào tài khoản Entity của từng bản đồ. Khác với tài khoản người chơi, tài khoản hệ thống cho phép số dư âm, nhưng khi xảy ra trường hợp này, hệ thống sẽ cảnh báo nhân viên vận hành để kiểm soát lượng tiền phát hành trong phó bản.

Khi hủy vật phẩm, hệ thống sẽ chuyển chủ sở hữu sang một “thùng rác” thay vì xóa khỏi cơ sở dữ liệu. Về mặt kỹ thuật, có thể tối ưu bằng cách di chuyển dữ liệu “nguội” (dữ liệu không còn được truy cập trong quy trình bình thường) sang cơ sở dữ liệu riêng biệt.

Giao diện hệ thống đơn giản, tập trung vào một yêu cầu duy nhất: ổn định và hiệu quả khi xử lý lượng dữ liệu khổng lồ. Với dữ liệu “nóng” (vật phẩm của người chơi đang online), cần áp dụng cơ chế cache. Với việc mỗi vật phẩm đều có ID duy nhất, quy mô đối tượng trong thế giới game dễ dàng vượt mốc 1 tỷ. Đặc biệt khi có kế hoạch hợp nhất máy chủ trong tương lai, cần đảm bảo không xảy ra trùng lặp ID giữa các máy chủ. Quy mô lớn như vậy đòi hỏi nhiều giải pháp tối ưu hiệu suất từ nhiều khía cạnh.

Về lựa chọn cơ sở dữ liệu, tôi cho rằng đây là vấn đề thứ yếu. Người thực hiện có thể tự quyết định dùng SQL hay NoSQL. Bạn Xiaojing đã chủ động nhận làm hệ thống này và chọn Redis để lưu trữ dữ liệu, tôi thấy đây là lựa chọn hoàn toàn khả thi.

Đối với phòng chống thảm họa dữ liệu, chúng tôi dự kiến ghi log từng giao dịch thông qua một dịch vụ độc lập. Bằng cách này, chỉ cần dựa vào log là có thể phục hồi toàn bộ cơ sở dữ liệu. Chúng tôi sẽ tập trung nâng cấp hệ thống log để đảm bảo dữ liệu không bị mất mát.

0%