Trong nhiều hệ thống hiện
đại, số lượng thao tác đọc thường lớn hơn rất nhiều so với thao tác ghi. Khi cả
hai cùng chia sẻ một cơ sở dữ liệu, hiệu năng nhanh chóng trở thành điểm nghẽn
và việc mở rộng hệ thống trở nên khó khăn.
Đó là lý do kiến
trúc Command Query Responsibility Segregation (CQRS) ra đời.
Thay vì sử dụng chung một mô hình dữ liệu, CQRS tách riêng luồng ghi và luồng đọc
để mỗi bên được tối ưu cho nhiệm vụ của mình. Đây là một trong những mẫu kiến
trúc quan trọng trong Cloud-Native Storage dành cho các hệ thống có quy mô lớn
và yêu cầu hiệu năng cao.
7. Command Query Responsibility Segregation (CQRS)
Command Query
Responsibility Segregation (CQRS) là một giải pháp cấp
cao để giải quyết sự xung đột giữa việc ghi và đọc dữ liệu trong các hệ thống
quy mô lớn.
Bản chất: "Chia để trị" giữa Đọc và Ghi
Thông thường, một ứng dụng
sẽ dùng chung một cơ sở dữ liệu (CSDL) để vừa tạo mới (Create), vừa đọc (Read),
cập nhật (Update) và xóa (Delete) dữ liệu. Tuy nhiên, triết lý cốt lõi của CQRS là
không dùng một túi dữ liệu duy nhất mà chia thành hai phần riêng biệt:
một bên chuyên để cập nhật dữ liệu (Command) và một bên chuyên để truy vấn dữ
liệu (Query).
Hệ thống sẽ giữ hai phần
này đồng bộ với nhau: hễ có thay đổi bên "Ghi", nó sẽ tự động cập nhật
sang bên "Đọc".
Tại sao phải làm phức tạp như vậy?
Trong thực tế, việc dùng
chung một CSDL gây ra ba vấn đề lớn:
- Xung đột hiệu suất: Khi
có quá nhiều người cùng cập nhật dữ liệu, hệ thống phải "khóa"
các bản ghi lại để đảm bảo tính chính xác, khiến những người muốn đọc dữ
liệu phải xếp hàng chờ đợi.
- Tối ưu hóa nửa vời: Rất
khó để thiết kế một CSDL vừa nhanh khi ghi, vừa nhanh khi đọc. Thường thì
việc tối ưu cho bên này sẽ làm hại bên kia.
- Góc nhìn khác nhau: Người
mua hàng muốn nhìn dữ liệu theo kiểu này, nhưng nhân viên kho lại muốn
nhìn dữ liệu theo kiểu khác. Một cấu trúc dữ liệu duy nhất khó lòng phục vụ
tốt cho tất cả.
Cách vận hành của bộ máy CQRS
1. Nhánh Ghi (Write
Solution): "Người thực thi mệnh lệnh"
Phía bên trái tập trung
vào việc thay đổi trạng thái của hệ thống:
- Updating Client & Modify API: Người dùng gửi yêu cầu thay đổi (tạo mới, cập nhật, xóa) thông qua một giao diện lập trình (API) chuyên dụng.
- Command Facade & Command Bus (Tùy chọn): Các yêu cầu được đóng gói thành các Lệnh (Commands). Nếu có quá nhiều yêu cầu cùng lúc, một "Command Bus" (Trục lệnh) sẽ đóng vai trò như một hàng chờ để xếp hàng các lệnh này, giúp hệ thống không bị quá tải.
- Write Model (Domain Model): Đây là "trái tim" của nhánh ghi, nơi chứa các quy tắc nghiệp vụ phức tạp. Nó xử lý các lệnh một cách tuần tự để đảm bảo dữ liệu luôn chính xác và hợp lệ.
- Database of Record: Đây là nơi lưu trữ "sự thật" duy nhất và mới nhất của hệ thống. Nhánh Ghi sẽ cập nhật trực tiếp vào đây.
- Change Log (Tùy chọn): Một nhật ký lưu lại lịch sử mọi thay đổi để có thể phục hồi hoặc kiểm tra khi cần.
2. Trục kết nối: Event
Backbone (Trục sự kiện)
Nằm ở giữa sơ đồ, Event
Backbone đóng vai trò là "người đưa tin". Mỗi khi Nhánh Ghi
cập nhật xong dữ liệu vào CSDL, nó sẽ phát ra một Sự kiện (Update
Event) thông báo rằng: "Này, dữ liệu X đã thay đổi rồi!".
3. Nhánh Đọc (Read
Solution): "Người phục vụ thông tin"
Phía bên phải sơ đồ tập
trung vào việc hiển thị dữ liệu cho người dùng một cách nhanh nhất:
- Event Processor: Thành phần này luôn lắng nghe các sự kiện từ Trục sự kiện. Khi thấy có thông báo thay đổi, nó sẽ cập nhật ngay lập tức vào Query Database để bên Đọc luôn có dữ liệu mới.
- Query Database (CSDL Truy vấn): Đây là một bản sao (replica) của "Database of Record" bên ghi nhưng được tối ưu hóa hoàn toàn cho việc tìm kiếm và báo cáo.
- Read Model: Thực hiện các câu lệnh truy vấn vào CSDL Truy vấn và đóng gói kết quả thành các Data Transfer Objects (DTOs) — là những cấu trúc dữ liệu gọn nhẹ, chỉ chứa đúng những gì người dùng cần xem.
- Retrieve API & Querying Client: Người dùng chỉ việc đặt câu hỏi (truy vấn) và nhận lại thông tin một cách nhanh chóng mà không làm ảnh hưởng đến hiệu suất của bên Ghi.
4. Tại sao cấu trúc này lại
mạnh mẽ?
Sơ đồ này giải quyết các
bài toán khó mà CSDL truyền thống thường "đầu hàng":
- Tối ưu hóa riêng biệt: Bạn có thể dùng CSDL Key-Value ở bên Ghi để đạt tốc độ cực nhanh, nhưng dùng CSDL Columnar (dạng cột) ở bên Đọc để tạo báo cáo khổng lồ chỉ trong vài giây.
- Không còn xung đột: Người đang sửa dữ liệu không làm chậm người đang xem dữ liệu và ngược lại, vì họ thao tác trên hai CSDL khác nhau.
- Khả năng mở rộng: Bạn có thể chạy 20 máy chủ cho bên Đọc (vì người dùng xem nhiều) nhưng chỉ cần 3 máy chủ cho bên Ghi.
5. Sự đánh đổi: Nhất quán
cuối cùng (Eventual Consistency)
Sơ đồ cũng ngầm chỉ ra một
thách thức: Vì dữ liệu phải đi qua Trục sự kiện để sang bên Đọc, nên sẽ có một
khoảng trễ cực ngắn (vài mili giây). Trong khoảnh khắc đó, người đọc có thể thấy
dữ liệu hơi "cũ" một chút so với người vừa ghi xong. Bạn chấp nhận điều
này để đổi lấy hiệu suất và quy mô siêu lớn của hệ thống đám mây.
CQRS cực kỳ hữu ích khi bạn
muốn hiện đại hóa một hệ thống cũ (Monolith) mà không thể đập đi xây lại toàn bộ.
Bạn có thể giữ hệ thống cũ làm nhánh Ghi và xây dựng một nhánh Đọc mới linh hoạt
hơn trên đám mây.
CQRS là một giải pháp kiến
trúc "hạng nặng" dành cho những bài toán khó, nơi mà sự tách biệt giữa
hành động thay đổi thế giới (Ghi) và hành động quan sát thế giới (Đọc) mang lại
hiệu suất và khả năng mở rộng không giới hạn.
Kết luận

Comments
Post a Comment