0
0
Lập trình
Admin Team
Admin Teamtechmely

Cách Triển Khai Ledger Trong Hệ Thống Phân Tán

Đăng vào 7 giờ trước

• 5 phút đọc

Giới Thiệu Về Ledger

Ledger, hay còn gọi là "sổ cái", là một khái niệm quan trọng trong hệ thống tài chính và công nghệ thông tin. Hãy tưởng tượng một Ledger giống như một bảng tính kế toán của một công ty, nơi mọi giao dịch đều được ghi lại. Điều này đảm bảo tính immutability (không thể thay đổi), một nguyên tắc quan trọng trong hệ thống tài chính. Để duy trì tính imutability, chúng ta thường chỉ thêm các mục mới mà không xóa hay sửa đổi các mục đã có.

Cách Triển Khai Ledger

Bước 1: Tạo Ledger Riêng Cho Mỗi Loại Tiền Tệ

Một trong những thực tiễn tốt nhất khi triển khai Ledger là duy trì một Ledger riêng biệt cho mỗi loại tiền tệ, chẳng hạn như VND hoặc USD. Điều này giúp đơn giản hóa logic kinh doanh và các phép toán. Đồng thời, để ngăn chặn việc xử lý trùng lặp trong hệ thống phân tán, mỗi giao dịch cần có một khóa idempotency duy nhất.

Bước 2: Quản Lý Tính Đồng Thời

Quản lý tính đồng thời là yếu tố then chốt để tránh những sai lệch trong số dư. Một phương pháp là sử dụng lock tối ưu (optimistic lock), trong đó sử dụng một trường như version hoặc last_sequence. Phương pháp thay thế là lock bi quan (pessimistic lock), đảm bảo quyền truy cập độc quyền vào Ledger thông qua một dịch vụ bên ngoài như Redis.

Persistencia và Tính Đồng Thời

Lưu Trữ Ledger Trong Cơ Sở Dữ Liệu

Ledger có thể được lưu trữ trong một cơ sở dữ liệu như MongoDB. Đối với việc xử lý lock tối ưu, có thể sử dụng một trường như last_sequence trong tài liệu của Ledger. Đối với lock bi quan, khuyến nghị sử dụng một dịch vụ cache phân tán với độ trễ thấp, chẳng hạn như Redis.

Khóa Idempotency

Khóa idempotency thường được tạo ra từ sự kết hợp của các dữ liệu như timestamp + from + to + amount và có thể được lưu trữ trong một cache phân tán với TTL (Thời gian sống).

Quản Lý Tiền Trong Hệ Thống Phân Tán

Tại Sao Nên Sử Dụng int Thay Vì float?

Khi làm việc với tiền, hãy luôn tránh sử dụng các kiểu số thực (float). Thay vào đó, hãy sử dụng số nguyên, lưu trữ giá trị ở đơn vị nhỏ nhất của đồng tiền (ví dụ: R$ 123,45 được lưu trữ dưới dạng số nguyên 12345). Tất cả các phép toán sẽ được thực hiện với số nguyên này, giúp loại bỏ lỗi về độ chính xác.

Cấu Trúc Đối Với Nhiều Loại Tiền Tệ

Giá trị tiền tệ nên được biểu diễn bởi một cấu trúc kết hợp giữa số tiền và loại tiền tệ, theo tiêu chuẩn ISO 4217. Cấu trúc này cần có amount (số nguyên) và currency (ví dụ: "VND"), ngăn chặn các lỗi như cộng trực tiếp đô-la với đồng Việt Nam.

Áp Dụng Lock Tối Ưu và Giao Dịch

Để đảm bảo tính nhất quán, quá trình ghi một giao dịch cần phải là nguyên tử. Dưới đây là hướng dẫn chi tiết về cách thực hiện quy trình này:

  1. Kiểm Tra Idempotency (Fail Fast): Trước khi bắt đầu giao dịch, hãy kiểm tra khóa idempotency trong cache nhanh như Redis. Nếu khóa đã tồn tại, điều này có nghĩa là giao dịch đã được xử lý, bạn có thể trả về thành công ngay lập tức, tránh công việc không cần thiết.

  2. Bắt Đầu Vòng Lặp Thử Nghiệm (Retry Loop): Vì lock tối ưu có thể thất bại, toàn bộ logic kinh doanh cần được thực hiện trong một vòng lặp sẽ thử lại một số lần (ví dụ: 3 lần) trước khi từ bỏ.

  3. Đọc Trạng Thái Hiện Tại: Trong vòng lặp và trong một giao dịch mới từ MongoDB, đọc tài liệu của Ledger để lấy balancelast_sequence hiện tại.

  4. Tính Toán Trong Bộ Nhớ: Tính toán số dư mới trong ứng dụng của bạn: new_balance = current_balance + transaction_value.

  5. Thực Hiện Ghi Nhớ Nguyên Tử: Thực hiện hai thao tác ghi trong cùng một giao dịch:

    • insertOne vào bộ sưu tập transactions với dữ liệu của giao dịch mới.
    • updateOne trong bộ sưu tập ledgers. Cập nhật này là chìa khóa của lock tối ưu: nó phải tìm _id last_sequence mà bạn đã đọc ở bước 3. Cập nhật sẽ sửa đổi balance và tăng last_sequence.
  6. Kết Quả:

    • Thành Công: Nếu updateOne tìm thấy tài liệu và giao dịch hoàn tất (commit), có nghĩa là không có xung đột. Bạn có thể thoát khỏi vòng lặp và trả về thành công.
    • Thất Bại: Nếu giao dịch thất bại vì last_sequence không khớp, có nghĩa là một quy trình khác đã thay đổi Ledger. Vòng lặp thử nghiệm sẽ tiếp tục cho lần lặp tiếp theo, bắt đầu lại từ bước 3. Lưu ý rằng nên thêm một khoảng thời gian chờ nhỏ (backoff) giữa các lần thử.

Nếu vòng lặp kết thúc mà không thành công, giao dịch đã thất bại và cần trả về lỗi cho khách hàng.

Cấu Trúc Dữ Liệu

Dữ Liệu Ledger

json Copy
{
 "_id": ObjectId(),
 "balance": {
    "amount": 12345,
    "currency": "VND"
 }, 
 "last_sequence": 1,
 "last_transactions":[
   {
     "_id": ObjectId(),
     "ledger_id": ObjectId(),
     "timestamp": UnixTime,
     "sequence": 1,
     "change": {
        "amount": 5000,
        "currency": "VND"
     },
     "idempotency_key": "timestamp-from-to-amount"
   }
 ]
}

Dữ Liệu Giao Dịch

json Copy
{
     "_id": ObjectId(),
     "ledger_id": ObjectId(),
     "timestamp": UnixTime,
     "sequence": 1,
     "change": {
        "amount": 5000,
        "currency": "VND"
     },
     "idempotency_key": "timestamp-from-to-amount"
}

Kết Luận

Xây dựng một Ledger đáng tin cậy dựa trên bốn yếu tố thiết yếu:
Immutability - đảm bảo lịch sử giao dịch chỉ là bổ sung;
Độ Chính Xác - sử dụng số nguyên cho các phép toán tài chính để tránh lỗi số thực;
Tính Nhất Quán - thông qua giao dịch nguyên tử với quản lý đồng thời như lock tối ưu;
Idempotency - bảo vệ hệ thống khỏi các giao dịch trùng lặp.

Khi những nền tảng này được củng cố, bước tiếp theo trong sự phát triển của hệ thống là xem xét về mở rộng quy mô. Đối với các Ledger có hàng triệu giao dịch, các kỹ thuật như Snapshots (ảnh chụp định kỳ số dư) để tối ưu hóa hiệu suất đọc và lưu trữ các giao dịch cũ trở nên rất quan trọng để duy trì hiệu suất của hệ thống lâu dài.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào