Phân mảnh RDBMS, DHT và Hệ sinh thái NoSQL
Khám phá kỹ thuật phân mảnh Sharding (Partitioning + Replication), phản mẫu ORM, thuật toán Băm Nhất quán DHT và hai nhánh tiến hóa kinh điển: Google Bigtable vs Amazon Dynamo.
Giới hạn Mở rộng RDBMS và Phản mẫu ORM
Trong phát triển phần mềm truyền thống, Cơ sở Dữ liệu Quan hệ (RDBMS) với mô hình giao dịch ACID thường được coi là giải pháp mặc định. Thế nhưng, Jonas Bonér đã chỉ ra hai rào cản chí mạng khi hệ thống mở rộng lên hàng triệu người dùng:
Khi sử dụng các thư viện ORM (như Hibernate, Entity Framework) kết hợp với mô hình thực thể có đồ thị quan hệ chằng chịt, một lệnh đọc tưởng chừng đơn giản có thể âm thầm kích hoạt hàng trăm câu truy vấn liên kết, nạp toàn bộ các bảng liên quan vào bộ nhớ. Jonas Bonér gọi đây là tình trạng "ôm trọn cả cơ sở dữ liệu vào lòng" (sitting with your whole database in your lap), khiến máy chủ ứng dụng sập vì lỗi Out-Of-Memory.
Bên cạnh đó, việc mở rộng quy mô ghi (Write Scalability) cho một RDBMS tập trung là bất khả thi do chi phí duy trì khóa giao dịch phân tán toàn cục. Để mở rộng RDBMS, kỹ sư bắt buộc phải áp dụng kỹ thuật Sharding (Phân mảnh).
Bảng Băm Phân tán (DHT) và Thuật toán Consistent Hashing
Để khắc phục nhược điểm của phép chia modulo khi thêm/bớt máy chủ, các hệ thống phân tán sử dụng Bảng Băm Phân tán (Distributed Hash Table - DHT) dựa trên thuật toán Băm Nhất quán (Consistent Hashing) (tiêu biểu là giao thức Chord và Pastry):
Trong Consistent Hashing, cả máy chủ (nodes) và dữ liệu (keys) đều được ánh xạ lên cùng một vòng tròn băm 32-bit hoặc 128-bit. Mỗi dữ liệu thuộc quyền quản lý của nút kế tiếp đầu tiên theo chiều kim đồng hồ.
- Virtual Nodes (Nút ảo): Mỗi nút vật lý được gán hàng trăm nút ảo trên vòng băm để đảm bảo dữ liệu được phân bố đồng đều, tránh hiện tượng nút mang tải quá lệch.
- Định tuyến O(log N): Bằng cách duy trì bảng ngón tay (Finger Table) trỏ tới các nút ở khoảng cách lũy thừa, bất kỳ nút nào cũng có thể tìm thấy nút đích chỉ trong
O(log N)bước nhảy.
Hai Nhánh Tiến hóa NoSQL: Bigtable vs Dynamo
Trên nền tảng phân tán của DHT, hệ sinh thái NoSQL phân tách thành hai nhánh kiến trúc lịch sử:
| Đặc tính | Nhánh Google Bigtable (2006) | Nhánh Amazon Dynamo (2007) |
|---|---|---|
| Mục tiêu cốt lõi | Hệ thống cơ sở dữ liệu có cấu trúc giàu tính năng trên hệ thống tập tin phân tán (GFS). | Bảng băm phân tán độ sẵn sàng tuyệt đối (Always Writable) cho giỏ hàng thương mại điện tử. |
| Triết lý CAP | CP (Ưu tiên tính nhất quán mạnh, dữ liệu tuyến tính hóa). | AP (Ưu tiên tính sẵn sàng cao, mô hình nhất quán cuối cùng BASE). |
| Cơ chế Phân tán | Nút Master điều phối phân vùng Region + Tablet Server. | Mạng ngang hàng Peer-to-Peer, không nút chủ, vòng Consistent Hashing. |
| Phát hiện Xung đột | Không có xung đột ghi (chỉ 1 Tablet Server quản lý mỗi dải khóa). | Đồng hồ Vector (Vector Clocks) và Sloppy Quorum với Hinted Handoff. |
| Hậu duệ tiêu biểu | Apache HBase, Hypertable, Google Cloud Bigtable. | LinkedIn Project Voldemort, Basho Riak, Dynomite. |
| Sự kết hợp hoàn hảo | Apache Cassandra (Facebook): Sử dụng vòng băm phân tán DHT và giao thức Gossip của Dynamo, kết hợp với mô hình lưu trữ họ cột (Column-Family) và tệp SSTable của Bigtable! | |
Phân bổ Sử dụng theo 5 Họ Cơ sở Dữ liệu NoSQL:
Mã nguồn Minh họa Vòng Consistent Hashing
Mẫu cài đặt thuật toán Consistent Hashing với kỹ thuật Virtual Nodes trong Python:
import hashlib
import bisect
class ConsistentHashRing:
def __init__(self, replicas=3):
self.replicas = replicas # Virtual nodes để phân bổ đều
self.ring = [] # Danh sách hash đã sắp xếp
self.node_map = {} # Hash -> Node name
def _hash(self, key):
return int(hashlib.md5(key.encode('utf-8')).hexdigest(), 16)
def add_node(self, node):
for i in range(self.replicas):
vnode_key = f"{node}#vnode{i}"
h = self._hash(vnode_key)
bisect.insort(self.ring, h)
self.node_map[h] = node
def get_node(self, key):
if not self.ring:
return None
h = self._hash(key)
idx = bisect.bisect_right(self.ring, h)
if idx == len(self.ring):
idx = 0 # Quay vòng về đầu (Ring)
return self.node_map[self.ring[idx]]
ring = ConsistentHashRing(replicas=100)
ring.add_node("Server-1")
ring.add_node("Server-2")
ring.add_node("Server-3")
print("User 1042 thuộc server:", ring.get_node("user_1042"))
print("User 8821 thuộc server:", ring.get_node("user_8821"))Các Trang Liên quan
- Nền tảng Thiết kế Hệ thống và Các Đánh đổi Cốt lõi — Scale Up vs Scale Out và tranh chấp tài nguyên.
- Định lý CAP, Mô hình ACID và BASE — Phân tích sự đánh đổi CP vs AP trong Bigtable và Dynamo.
- Các Mô hình và Topo Sao chép Dữ liệu — Sao chép phân đoạn shard để đảm bảo HA.
- Các Chiến lược Bộ nhớ Đệm Phân tán — Lưới dữ liệu phân tán (Data Grids) tăng tốc truy vấn cho NoSQL/RDBMS.