Trong bài viết này mình sẽ giải thích nguyên nhân khái niệm Distributed Transaction ra đời, đặt biệt trong bối cảnh microservices ngày càng phát triển. Các kiến trúc cũng như giao thức thông dụng giúp đảm bảo sự nhất quán cho Distributed Transaction.
Mình sẽ không nêu ra hết mà chỉ trình bày một vài phương pháp dễ hiểu và dễ triển khai. Những nội dung còn lại mình sẽ viết thành một bài viết khác vì chúng rất hay và nhiều kiến thức nên tách riêng ra sẽ hợp lí hơn.
1. Cách Transaction Thông Thường hoạt động
Transaction là một khái niệm đại diện cho một chuỗi các thao tác được thực hiện như một đơn vị thống nhất. Trong cơ sở dữ liệu, các transaction phải tuân thủ nguyên tắc ACID để đảm bảo tính toàn vẹn và nhất quán của dữ liệu.
Nguyên tắc ACID bao gồm bốn thuộc tính quan trọng, đảm bảo dữ liệu luôn an toàn và thống nhất khi thực hiện các thao tác trong cơ sở dữ liệu. Nếu các bạn chưa hiểu rõ về ACID có thể tham khảo bài viết này trước khi tiếp tục nhé.
Những nguyên tắc này đảm bảo rằng dữ liệu luôn an toàn và đáng tin cậy, đặc biệt là trong các tình huống lỗi hệ thống hoặc xử lý đồng thời nhiều transaction. Hầu hết các hệ quản trị cơ sở dữ liệu đều ghi nhận transaction logs để ghi lại các thay đổi và hỗ trợ khôi phục trong trường hợp cần thiết.
2. Distributed Transaction là gì?
Distributed transaction là transaction liên quan đến nhiều hệ thống hoặc tài nguyên khác nhau nằm trên các máy chủ riêng biệt hoặc thậm chí ở các vị trí địa lý khác nhau.
Distributed transactions bắt nguồn từ sự phát triển của hệ thống phân tán vào cuối những năm 1970 và đầu 1980, khi các tổ chức cần tích hợp và phối hợp hoạt động giữa các máy tính độc lập, thường cách xa nhau về mặt địa lý.
Nhu cầu này phát sinh từ sự tăng trưởng về quy mô và độ phức tạp của hệ thống, khi dữ liệu và logic kinh doanh không thể chỉ nằm trên một máy tính hoặc cơ sở dữ liệu duy nhất. Các nhiệm vụ trong ứng dụng được chia nhỏ thành các service hay còn gọi là microservices, mỗi microservice lại được triển khai trên nhiều node khác nhau.
Khi dữ liệu được lưu trữ và xử lý trên nhiều hệ thống, việc đảm bảo tính nhất quán trở thành thách thức lớn. Nói cách khác khi chúng ta giải quyết được bài toán chia tải (nhằm tăng khả năng chịu tải) thì lại gặp phải bài toán về data consistency.
Vậy hãy cùng mình đi vào ví dụ về Hệ thống đặt vé máy bay (Airline Reservation System)
để bạn dễ hiểu nhé.
Khi khách hàng đặt vé máy bay:
- Hệ thống cần khóa chỗ trên chuyến bay.
- Trừ tiền trong tài khoản ngân hàng của khách hàng.
- Gửi thông báo xác nhận đặt vé.
Các tài nguyên tham gia vào quá trình đặt vé nằm ở nhiều hệ thống khác nhau bao gồm:
- Hệ thống quản lý chuyến bay (sử dụng Database riêng).
- Hệ thống ngân hàng (sử dụng Database riêng).
- Hệ thống quản lý thông báo (sử dụng Database riêng).
Distributed transaction được sử dụng để đảm bảo rằng chỗ ngồi chỉ bị giữ khi tiền đã được thanh toán thành công. Nếu thanh toán thất bại, chỗ ngồi sẽ được giải phóng ngay lập tức. Với transaction thông thường tất cả thông tin nằm trong cùng một Database, bạn chỉ cần BEGIN TRANSACTION, COMMIT
là đủ. Với kiến trúc phân tán, đây rõ ràng là một thách thức.
3 Các giải pháp Distributed Transaction phổ biến
Nếu các bạn đang thắc mắc có bao nhiêu giải pháp để đảm bảo sự nhất quán cho Distributed Transaction thì câu trả lời là rất nhiều :
- Blocking Retry, Two-Phase Commit (2PC) and Three-Phase Commit (3PC).
- Sử dụng Queues để xử lý Asynchronously trong Background, TCC. Compensation Matters.
- Local Message Table (Asynchronously Ensured)/Outbox Pattern, MQ Transaction.
- Saga Pattern, Event Sourcing, CQRS, Atomic Commitment.
- Parallel Commits, Transactional Replication, Consensus Algorithms.
- Timestamp Ordering, Optimistic Concurrency Control, Byzantine Fault Tolerance (BFT).
- Distributed Locking, Sharding, Multi-Version Concurrency Control (MVCC).
- Distributed Snapshots, Leader-Follower Replication
Vì thế mình sẽ chỉ điểm qua những giải pháp nào hay được sử dụng bởi các Datatabase (tầng lưu trữ) lớn trên thị trường và các mô hình kiến trúc (tầng ứng dụng) hỗ trợ Distributed Transaction.
3.1 Two-Phase Commit (2PC)
Two-Phase Commit (2PC) là một thuật toán phân tán được sử dụng để đảm bảo tính atomicity và tính consistency của các transaction trên nhiều cơ sở dữ liệu hoặc tài nguyên trong một hệ thống phân tán.
- Prepare Phase:
- Các participant nhận được yêu cầu bắt đầu transaction từ coordinator nhưng chưa thực hiện commit.
- Tài nguyên sẽ bị khóa ngay khi transaction bắt đầu.
- Việc này ngăn chặn các yêu cầu khác truy cập vào tài nguyên đã bị khóa.
- Commit Phase:
- Nếu tất cả các participants phản hồi Yes, coordinator gửi thông báo commit đến tất cả các participants, yêu cầu họ commit transaction.
- Nếu bất kỳ participant nào phản hồi No hoặc nếu coordinator không nhận được phản hồi từ participant nào trong thời gian quy định, coordinator gửi thông báo hủy (abort) đến tất cả participants, yêu cầu họ rollback.
Giao thức 2PC có khả năng đảm bảo tính nhất quán mạnh trong các hệ thống phân tán. Tất cả các node tham gia hoặc cùng hoàn thành hoặc cùng hủy bỏ transaction, giúp duy trì tính toàn vẹn của dữ liệu ngay cả khi các node hoạt động độc lập. 2PC rất phù hợp với các ứng dụng yêu cầu độ chính xác cao, như hệ thống tài chính hoặc ngân hàng, việc xử lý chính xác từng giao dịch là ưu tiên hàng đầu.
Tuy nhiên, giao thức 2PC cũng tồn tại một số hạn chế đáng kể. Hiệu suất của nó bị ảnh hưởng bởi số lượng lớn thông điệp trao đổi giữa các node, đồng thời việc khóa tài nguyên trong quá trình thực hiện transaction làm giảm khả năng xử lý song song.
2PC có khả năng chịu lỗi kém (single point of failure) , khi Coordinator bị lỗi trong giai đoạn commit phase (rớt mạng, bị treo) , sẽ dẫn đến tình trạng các node participant phải chờ vô thời hạn vì không biết là nên rollback hay commit. Giao thức này cũng không phù hợp với các hệ thống lớn hoặc mạng không ổn định (điều bình thường), do chi phí giao tiếp và độ trễ tăng nhanh theo số lượng node tham gia. Những hạn chế này khiến 2PC không phải là lựa chọn tối ưu cho những hệ thống yêu cầu cao về thời gian phản hồi.
Mình nghĩ các bạn cũng có thể đoán được các Database phổ biến hỗ trợ 2PC rồi đúng không, đó là: Oracle, Microsoft SQL Server, MySQL, PostgreSQL, ...
3.2 Three-Phase Commit (3PC)
Three-Phase Commit (3PC) là phiên bản mở rộng của giao thức Two-Phase Commit (2PC), được thiết kế để khắc phục tình trạng chặn (blocking) có thể xảy ra trong prepare phase. Do các bước của 3PC cũng khá giống vơi 2PC nên mình chỉ giải thích điểm khác biệt giữa hai phương thức này mà không ghi lại nội dung chi tiết các bước.
Nếu như coordinator bị timeout trong giai đoạn DoCommit thì tất cả participant sẽ đều thực hiện commit thay vì phải chờ, vì họ đã nhận được thông tin trong giai đoạn pre-commit là có thể thực hiện được, nhờ trao đổi nhiều thông tin hơn mà giao thức này có thể tự thực hiện được việc commit hay rollback ngay cả khi coordinator bị lỗi.
4. Kết luận
Hy vọng bài viết có thể giúp bạn nắm được danh sách các kĩ thuật xử lý Distributed Transaction trong hệ thống phân tán.
Các bài viết liên quan:
- Cassandra là gì? Vì sao Facebook tạo ra Cassandra
- Idempotent Consumer: Xử lý thông điệp trùng lặp trong Microservices
- CQRS là gì? Giới thiệu Design Pattern Command Query Responsibility Segregation
- Domain-Driven Design (DDD) là gì? Ví dụ dễ hiểu về DDD
Bài viết liên quan
Cassandra là gì? Vì sao Facebook tạo ra Cassandra
Dec 27, 2024 • 7 min read
Zod là gì? Hướng dẫn Validation với Zod
Dec 26, 2024 • 10 min read
Crontab là gì? Hướng dẫn sử dụng Crontab
Dec 20, 2024 • 4 min read
Yup là gì? Hướng dẫn Validation với Yup trong dự án React
Dec 19, 2024 • 14 min read
Tìm hiểu Sentry: Công cụ Theo dõi Lỗi và Hiệu suất tự động
Dec 16, 2024 • 7 min read
Tìm hiểu toàn diện về Index trong MySQL và PostgreSQL
Dec 14, 2024 • 12 min read