+1

Xử lý Transaction trong Microservices: Khi nào dùng Saga, khi nào dùng 2PC?

Trong kiến trúc Monolith truyền thống, quản lý Transaction (giao dịch) giống như việc bạn đi mua hàng tại tiệm tạp hóa đầu ngõ: Bạn đưa tiền, chủ hàng đưa món đồ. Mọi thứ diễn ra ngay lập tứctại chỗ. Nếu bạn thiếu một đồng hoặc chủ hàng hết hàng, giao dịch hủy bỏ, tiền vẫn trong túi bạn và hàng vẫn trên kệ. Đó là tính chất ACID thuần túy mà chúng ta luôn tin tưởng.

Nhưng khi bước sang thế giới Microservices, câu chuyện lại giống như bạn đang đặt hàng online trên sàn thương mại điện tử:

  • Order Service: Ghi nhận đơn hàng.
  • Payment Service: Trừ tiền trong ví điện tử của bạn.
  • Inventory Service: Kiểm tra và trừ tồn kho.
  • Shipping Service: Gọi đơn vị vận chuyển.

Vấn đề nằm ở chỗ: Mỗi bước trên được quản lý bởi một dịch vụ độc lập với Database riêng biệt. Chuyện gì sẽ xảy ra nếu tiền của bạn đã bị trừ (Bước 2), nhưng đến khi kiểm tra kho (Bước 3) thì món hàng đó vừa hết? Làm sao để "hoàn tiền" một cách tự động khi không có một Database chung nào quản lý toàn bộ quy trình này?

Đó là lúc cần tới Two-Phase Commit (2PC)Saga Pattern để giải quyết.

Giờ hãy cùng đi phân tích sâu hơn về hai cơ chế này, để biết khi nào nên chọn sự nhất quán tuyệt đối của 2PC và khi nào nên tin dùng sự linh hoạt của Saga.

1. Two-Phase Commit (2PC) - Sự nhất quán tuyệt đối

image.png

2PC là cơ chế đảm bảo tính nhất quán mạnh (Strong Consistency). Nó hoạt động thông qua một bộ điều phối trung tâm (Coordinator) theo hai giai đoạn:

  • Giai đoạn 1 (Prepare Phase)::
    • Xác nhận sẵn sàng: Coordinator hỏi tất cả các Service trong quy trình "có sẵn sàng để hoàn thành tiến trình không?"
    • Phản hồi: Các Service sẽ kiểm tra tính khả dụng của tiến trình, lock database và trả lời "Yes" hoặc "No" cho Coordinator
  • Giai đoạn 2 (Commit/Rollback Phase): * Nếu tất cả trả lời "Yes", Coordinator ra lệnh cho tất cả thực hiện Commit.
    • Chỉ cần một Service trả lời "No" hoặc không phản hồi, Coordinator ra lệnh cho tất cả Rollback để giải phóng tài nguyên.

Ví dụ:

khi bạn thực hiện thanh toán đơn hàng mua Iphone 17 promax thì hệ thống sẽ thực hiện:

  • Coordinator sẽ gửi yêu cầu Prepare (chuẩn bị) cho các service tham gia vào giao dịch
  • Sau đó các service khi nhận được yêu cầu Prepare:
    • Về dữ liệu: Kiểm tra xem còn hàng trong kho không, Số dư tài khoản có đủ không,...
    • Về kỹ thuật: Database có kết nối ổn định không, phần cứng có quá tải, bản ghi đó có đang được bên nào lock để dùng không,...
  • Nếu tất cả kiểm tra xong và báo về "YES" thì Coordinator sẽ bắt đầu ra lệnh thực thi tiến trình.

Tất nhiên là các service sẽ không thực hiện update trực vào DB khi tiến trình đang thực thi mà chỉ lock các dữ liệu liên quan và ghi log ra vào đâu đó (có thể là file tạm thời trên đĩa, 1 service log chuyên dụng,...) để đảm bảo kể cả service bị sập thì khởi động lại vẫn có thể tiếp tục tiến trình.

Ưu điểm:

  • Đảm bảo dữ liệu luôn khớp nhau 100% tại mọi thời điểm (ACID).
  • Lập trình viên không cần viết logic hoàn tác (rollback) thủ công ở tầng ứng dụng.

Nhược điểm:

  • Hiệu năng kém: Do phải lock tài nguyên (database rows) cho đến khi tất cả Service hoàn tất, gây ra hiện tượng nghẽn (blocking).
  • Single Point of Failure: Nếu bộ Coordinator bị sập trong quá trình chờ, các service khác sẽ rơi vào trạng thái "lửng lơ" vì đang giữ lock.
  • Khả năng mở rộng thấp: Càng nhiều service tham gia, tỷ lệ thất bại và thời gian chờ càng tăng cao.

2. Saga Pattern - Sự linh hoạt và bền bỉ

image.png

Nếu 2PC giống như một cuộc họp mà mọi người phải ngồi im cho đến khi có quyết định cuối cùng, thì Saga lại giống như một dây chuyền sản xuất. Mỗi Service thực hiện xong phần việc của mình rồi chuyển cho Service tiếp theo.

Cơ chế cốt lõi: Local Transactions & Compensating

Saga chia một giao dịch lớn thành một chuỗi các giao dịch cục bộ (Local Transactions). Mỗi bước này đều commit trực tiếp vào database của Service đó.

  • Hành động (Action): Thực hiện nghiệp vụ (ví dụ: Trừ tiền ví).
  • Hành động bù trừ (Compensating Transaction): Nếu bước sau thất bại, hệ thống phải thực hiện một hành động ngược lại để Hoàn trả trạng thái cho bước trước (ví dụ: Cộng lại tiền vào ví).

Hai hình thức triển khai chính:

Choreography (Tự điều phối)

Các Service giao tiếp với nhau qua Message Broker (như Kafka hoặc RabbitMQ). Mỗi Service xong việc sẽ bắn ra một Event, Service tiếp theo nghe thấy Event đó thì tự động chạy.

  • Cách vận hành:
    1. Order Service: Tạo đơn hàng -> Bắn Event Order_Created.
    2. Payment Service: Nghe thấy Order_Created -> Trừ tiền -> Bắn Event Payment_Done.
    3. Inventory Service: Nghe thấy Payment_Done -> Trừ kho -> Xong!
Ưu điểm:

Hệ thống cực kỳ linh hoạt (Decoupled), dễ dàng thêm Service mới mà không cần sửa code cũ.

Nhược điểm

Cực kỳ khó kiểm soát luồng khi hệ thống lớn. Nếu có lỗi xảy ra, việc truy vết (trace) xem "ai đã bắn event gì, tại sao sai" thì đúng là một cơn ác mộng.

Orchestration (Tập trung điều phối)

Sử dụng một bộ điều phối trung tâm (Saga Orchestrator) để quản lý luồng đi và trạng thái của giao dịch.

  • Cách vận hành: Orchestrator gửi lệnh tới từng Service. Nếu một Service báo "Thất bại", Orchestrator sẽ chủ động gửi lệnh "Rollback" cho những Service đã chạy trước đó.
Ưu điểm:
  • Quản lý tập trung: Nhìn vào Orchestrator là biết giao dịch đang dừng ở đâu.
  • Tránh được tình trạng "vòng lặp vô tận" của các Event.
Nhược điểm:
  • Phải quản lý thêm một bộ logic điều phối (Nhạc trưởng), nếu không thiết kế khéo, bộ này sẽ trở nên rất cồng kềnh.

3. So sánh nhanh: 2PC vs. Saga

Đặc điểm Two-Phase Commit (2PC) Saga Pattern
Tính nhất quán Strong Consistency (Tức thời) Eventual Consistency (Sau một khoảng thời gian)
Cơ chế Đồng bộ (Synchronous) Bất đồng bộ (Asynchronous)
Tài nguyên Lock tài nguyên (Blocking) Không lock tài nguyên (Non-blocking)
Độ phức tạp Thấp về mặt logic code ứng dụng Cao (phải thiết kế hàm bù trừ - compensation)
Khả năng mở rộng Kém Rất tốt

4. Khi nào chọn cái nào?

Việc lựa chọn phụ thuộc vào việc bạn ưu tiên Consistency (Nhất quán) hay Availability (Sẵn sàng):

  • Hãy chọn 2PC khi: xây dựng hệ thống lõi tài chính, ngân hàng yêu cầu dữ liệu phải chính xác tuyệt đối ngay lập tức và số lượng service tham gia ít (thường là 2-3 service).
  • Hãy chọn Saga khi: làm hệ thống quy mô lớn như thương mại điện tử, đặt vé máy bay, hoặc giao đồ ăn. Người dùng có thể chấp nhận việc đơn hàng ở trạng thái "Đang xử lý" trong vài giây trước khi hệ thống khớp hết dữ liệu.

Kết luận

Trong thế giới Microservices, chúng ta buộc phải làm quen với khái niệm Eventual Consistency (Nhất quán sau). Saga Pattern đang dần trở thành lựa chọn ưu tiên vì nó giúp hệ thống chịu tải tốt và linh hoạt hơn. Tuy nhiên, đừng bao giờ gạt bỏ 2PC nếu nghiệp vụ của bạn yêu cầu một sự đảm bảo "tất cả hoặc không có gì" một cách khắt khe.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí