Database scale: replication, sharding và NewSQL
Bậc thang scale database quan hệ: tune và index, máy to hơn, read replica, partitioning, sharding bằng ứng dụng hoặc middleware, rồi distributed SQL (NewSQL). Mỗi bậc giải quyết vấn đề gì, trả giá gì, khi nào lên bậc tiếp theo và khi nào đừng scale ngang.
Bậc thang scale database
Gần như mọi hệ thống bắt đầu với một database quan hệ, thường là Postgres hoặc MySQL. Câu hỏi trong phỏng vấn và ngoài đời giống nhau: khi nào một máy không còn đủ, và bước tiếp theo là gì. Cách trả lời tốt là một bậc thang. Bạn chỉ leo bậc kế tiếp khi bậc hiện tại đã hết dư địa, vì mỗi bậc lên cao đều tăng độ phức tạp vận hành.
Ba nhóm nút thắt đẩy bạn lên bậc: dung lượng dữ liệu, tốc độ đọc và tốc độ ghi. Grokking nói cả ba là lý do chia dữ liệu: tải đọc, tải ghi và lượng dữ liệu đều tăng, còn replication một mình khó đáp ứng đồng thời tính khả dụng, khả năng scale và hiệu năng.1 Bài này đi lần lượt qua từng bậc. Phần khái niệm sâu về chia dữ liệu nằm ở Sharding và Consistent hashing. Chọn engine cụ thể xem Postgres và So sánh database quan hệ.
Đi qua từng bậc
Phần này đi lần lượt qua sáu bậc, từ rẻ nhất đến đắt nhất.
Bậc 1 và 2: tune, index và máy to hơn
Trước khi nghĩ tới kiến trúc, hãy làm những việc rẻ nhất.
- Index đúng chỗ. Một query quét toàn bảng thường chậm hơn gấp nhiều lần so với dùng index. Xem Database index.
- Sửa query xấu. N+1 query,
SELECT *, thiếuLIMIT, sort không có index. - Connection pool giữa app và database để không tạo quá nhiều kết nối.
- Cache kết quả đọc nóng ở Redis, xem Caching.
- Scale up: thêm CPU, RAM, đĩa NVMe. Một máy lớn xử lý được nhiều hơn người ta tưởng.
Về con số, Grokking dùng mốc khoảng 1.000 QPS cho một server MySQL, và khoảng 10.000 QPS cho một server key-value, làm cận trên khi tính nhẩm.2 Quy tắc của khoá học cho Postgres 4 vCPU và 16 GB là vài nghìn QPS đọc đơn giản có index, và một node thoải mái đến khoảng 1 đến 2 TB (ước lượng, cần load test).3 Với vài chục nghìn người dùng, bạn thường chưa cần rời bậc này.
Mẹo: Scale up có trần và một điểm lỗi duy nhất, nhưng nó không đòi đổi code. Khi nhóm chỉ có vài người, "máy to hơn và backup tốt" thường là quyết định kỹ thuật đúng nhất.
Bậc 3: read replica
Khi đọc nhiều hơn ghi, thêm các bản sao chỉ đọc. Một primary nhận mọi lần ghi, các secondary nhận bản sao và phục vụ đọc. Grokking nói mô hình primary-secondary hợp với tải đọc nhiều, thêm follower để chia tải đọc, nhưng quá nhiều follower làm primary thành nút nghẽn và mô hình không hợp với tải ghi nhiều.4 Secondary còn đọc được khi primary hỏng, nên đây cũng là bước tăng độ sẵn sàng.4
Building Microservices cũng xếp read replica là cách scale đọc phổ biến, vì phần lớn hệ thống đọc nhiều hơn ghi.5
Replication lag
Có hai cách đẩy thay đổi sang replica. Đồng bộ thì primary chờ mọi secondary xác nhận, các bản sao luôn mới nhưng một secondary chậm hoặc lỗi làm cả lần ghi chậm theo. Bất đồng bộ thì primary báo thành công ngay sau khi tự ghi, nhưng nếu primary chết, các lần ghi chưa kịp sao chép sẽ mất.6 Hầu hết hệ thống chọn bất đồng bộ, nên replica luôn trễ so với primary một chút. Grokking liệt kê "xử lý replication lag" là một khó khăn riêng của replication bất đồng bộ.7
Hệ quả cho người dùng: vừa lưu hồ sơ, tải lại trang, đọc từ replica chưa cập nhật và thấy dữ liệu cũ. Đây là vấn đề read-your-writes. Cách xử lý phổ biến là sau khi người dùng ghi, một khoảng ngắn các lần đọc của chính người đó đi thẳng tới primary, còn lại đọc từ replica.8 Nói ngắn, replica chỉ phù hợp với dữ liệu chịu được "hơi cũ" (liên hệ CAP).
// Chọn pool theo ngữ cảnh: ghi và đọc-sau-ghi dùng primary, còn lại dùng replica
async function getProfile(userId: string, recentlyWrote: boolean) {
const pool = recentlyWrote ? primaryPool : replicaPool;
const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [userId]);
return rows[0];
}
Giới hạn lớn nhất của bậc này: ghi vẫn dồn vào một primary, và mỗi replica vẫn giữ toàn bộ dữ liệu. Khi ghi hoặc dung lượng vượt sức một máy, replica không giúp được nữa.
Bậc 4: partitioning trong một database
Partitioning chia một bảng lớn thành nhiều phần nhỏ trong cùng một database server, thường theo khoảng thời gian hoặc theo hash của một cột. Query có điều kiện trên cột chia chỉ chạm phần liên quan, xoá dữ liệu cũ nhanh bằng cách bỏ cả phần. Thư viện không có sách về cơ chế partitioning của Postgres hay MySQL, nên bài chỉ nêu ý chung.9
Điểm cần nhớ: partitioning không thêm máy. Nó giúp bảo trì, index nhỏ hơn và query hẹp nhanh hơn, nhưng mọi partition vẫn dùng chung CPU, RAM, đĩa của một node. Nó là bước đệm tốt trước khi shard, và thiết kế partition key hợp lý hôm nay cũng chính là bài tập chọn shard key cho ngày mai.
Bậc 5: sharding bằng ứng dụng hoặc middleware
Khi một primary không chịu nổi lượng ghi hoặc dung lượng, bạn chia dữ liệu ra nhiều database độc lập, mỗi cái là một shard với primary và replica riêng. Grokking mô tả horizontal sharding là chia theo dòng, mỗi phần gọi là một shard, có thể theo khoảng key hoặc theo hash.10 MongoDB TDG mô tả cùng ý: sharding là chia dữ liệu qua nhiều máy, có tiến trình router giữ "mục lục" để chuyển request tới đúng shard.11
Có hai cách đặt logic định tuyến:
| Cách | Mô tả | Ưu | Nhược |
|---|---|---|---|
| Sharding trong ứng dụng | Code app tính shard = hash(user_id) mod N rồi chọn connection | Rẻ, toàn quyền kiểm soát, không thêm thành phần | Logic nằm rải rác trong code, resharding đau, mỗi service phải biết |
| Middleware kiểu Vitess | Một lớp proxy đứng giữa app và các MySQL shard, app nói chuyện như với một database | Giấu topology, hỗ trợ resharding online, gom connection | Thêm thành phần để vận hành, một phần SQL bị hạn chế |
| Extension kiểu Citus | Biến Postgres thành cụm có coordinator và worker, bảng phân tán theo một cột | Vẫn là Postgres, ít đổi code | Query và join xuyên node cần cẩn thận, chọn cột phân tán là quyết định lớn |
Hai tên Vitess và Citus là ví dụ về middleware và extension chia dữ liệu; thư viện không có sách về chúng nên bài không đi vào cơ chế hay giới hạn cụ thể.12
Cái giá của bậc này, đã nói kỹ ở Sharding:
- Shard key là quyết định khó đổi nhất. MongoDB TDG viết khi đã có hơn vài shard thì gần như không thể đổi shard key.13
- Query không có shard key phải hỏi mọi shard rồi gộp (scatter-gather).11
- Join và transaction xuyên shard gần như không còn miễn phí (xem phần riêng bên dưới).
- Resharding khi thêm shard: dùng consistent hashing để chỉ di chuyển một phần dữ liệu.14
Bậc 6: distributed SQL và NewSQL
Sharding bằng tay cho bạn scale ngang nhưng bạn tự gánh phần khó. Distributed SQL, còn gọi NewSQL, cố giữ giao diện SQL và transaction ACID trong khi tự động chia dữ liệu và nhân bản. Grokking nhắc đến hướng này khi nêu rằng sự khác biệt về cách lập trình giữa NoSQL và database truyền thống đang mờ dần, và lấy Google Cloud Spanner làm ví dụ: geo-replicated, tự động sharding ngang, có snapshot toàn cầu tốc độ cao.15
Các sản phẩm tiêu biểu là Google Spanner, CockroachDB, TiDB và YugabyteDB. Thư viện không có sách về các hệ này, nên phần dưới chỉ nêu ý tưởng chung, không có số liệu, phiên bản hay giá.16
Ý tưởng chung:
- Chia dữ liệu thành nhiều range (khoảng key liên tiếp). Mỗi range nhỏ, tự động tách khi lớn lên và được di chuyển giữa các node để cân bằng tải.
- Mỗi range là một nhóm đồng thuận (consensus, thường Raft hoặc Paxos): có một leader nhận ghi, ghi chỉ thành công khi đa số replica trong nhóm xác nhận. Mất một node thì nhóm bầu leader mới, không mất dữ liệu đã commit.
- Transaction phân tán: một transaction chạm nhiều range chạy một dạng commit hai pha trên các nhóm đồng thuận, với cơ chế timestamp để có snapshot nhất quán. Spanner nổi tiếng vì dùng đồng hồ có độ sai số được kiểm soát; các hệ khác dùng cách khác.
- Lớp SQL ở trên: parser và planner biến SQL thành đọc/ghi trên các range. Nhiều hệ giả lập giao thức của Postgres hoặc MySQL để dùng lại driver cũ.
Đánh đổi của NewSQL
| Điểm | Ảnh hưởng |
|---|---|
| Độ trễ ghi | Mỗi lần ghi cần đa số replica xác nhận, thêm ít nhất một round trip mạng. Triển khai nhiều vùng thì độ trễ liên vùng (VN tới Singapore khoảng 30 đến 50 ms, tới US khoảng 150 đến 250 ms, theo quy tắc của khoá học) nằm trong đường ghi.3 |
| Transaction xuyên range | Chậm hơn transaction trong một node, vì thêm vòng phối hợp |
| Chi phí | Cần tối thiểu ba node cho mỗi nhóm đồng thuận, tốn tài nguyên hơn một Postgres có replica; chạy managed thì trả thêm phí dịch vụ |
| Tương thích SQL | Gần giống Postgres hoặc MySQL nhưng không phải 100%: một số extension, kiểu dữ liệu, cách khoá và mức isolation khác nhau |
| Vận hành | Ít việc hơn sharding tay (resharding tự động), nhưng cần hiểu hot range, vị trí leader, chọn primary key |
Các đánh đổi trên là nhận định tổng quát của khoá học dựa trên mô hình đồng thuận, chưa có sách kiểm chứng từng sản phẩm.16
Sai lầm hay gặp: Nghĩ NewSQL "thay Postgres mà không phải đổi gì". Primary key tăng dần (auto-increment) vẫn tạo hot range, query không theo key vẫn phải quét nhiều range, và độ trễ ghi cao hơn single node. Hãy đo trên workload thật trước khi chuyển.
So sánh các bậc
| Bậc | Giải quyết | Còn giới hạn | Độ phức tạp | Đổi code app |
|---|---|---|---|---|
| Tune và index | Query chậm | Trần của một máy | Rất thấp | Ít |
| Scale up | Thiếu CPU, RAM, đĩa | Trần phần cứng, một điểm lỗi | Thấp | Không |
| Read replica | Đọc nhiều | Ghi và dung lượng vẫn ở một primary, replication lag | Thấp đến vừa | Vừa (tách đường đọc, ghi) |
| Partitioning | Bảng khổng lồ, bảo trì | Vẫn một node | Vừa | Ít |
| Sharding app/middleware | Ghi và dung lượng vượt một node | Cross-shard query và transaction, resharding | Cao | Nhiều (app) hoặc ít (middleware) |
| NewSQL | Scale ngang với SQL và ACID, đa vùng | Độ trễ ghi, chi phí, tương thích | Vừa đến cao | Ít đến vừa |
Bảng tổng hợp từ các phần trên: replica và sharding theo Grokking ch.9, NewSQL theo ghi chú Spanner.41015
Transaction xuyên shard: 2PC hay saga
Trong một database đơn, BEGIN ... COMMIT lo hết. Khi dữ liệu nằm trên nhiều shard hoặc nhiều database, bạn có hai hướng.
Two-phase commit (2PC). Một coordinator hỏi mọi bên "sẵn sàng commit chưa" (pha vote), nếu tất cả đồng ý thì ra lệnh commit. Building Microservices ch.5 nêu các vấn đề: nếu coordinator chết hoặc một bên không phản hồi thì transaction bị treo, và việc giữ khoá trong lúc chờ gây tranh chấp, làm hệ thống khó scale.17 Microservices Patterns §4.1 kết luận 2PC không phải lựa chọn khả thi cho ứng dụng hiện đại.18
Saga. Chia thao tác thành chuỗi transaction cục bộ, mỗi bước commit ngay trong shard của nó. Nếu một bước thất bại, chạy các transaction bù (compensating) để hoàn tác các bước trước. Microservices Patterns ch.4 mô tả saga là cách duy trì nhất quán dữ liệu mà không cần distributed transaction, nhưng nó thiếu tính isolation của ACID và phức tạp hơn để viết đúng.18 Chi tiết xem Quy trình nhiều bước.
| 2PC | Saga | |
|---|---|---|
| Nhất quán | Mạnh, atomic | Cuối cùng, có trạng thái trung gian nhìn thấy được |
| Khoá | Giữ khoá suốt hai pha | Không giữ khoá xuyên bước |
| Khi lỗi | Có thể treo | Chạy bù |
| Độ phức tạp | Nằm ở hạ tầng | Nằm ở code ứng dụng |
NewSQL đưa 2PC xuống tầng database (kết hợp với đồng thuận để coordinator không còn là điểm chết đơn). Đó là một lý do người ta trả giá latency và chi phí để dùng nó thay vì tự viết saga. Nhưng cách rẻ nhất vẫn là thiết kế shard key sao cho transaction quan trọng nằm gọn trong một shard, ví dụ shard theo user_id thì mọi thứ của một user ở cùng nơi.
CAP và PACELC
Mỗi bậc đổi một chút giữa nhất quán, sẵn sàng và độ trễ. Read replica bất đồng bộ cho đọc nhanh nhưng có thể đọc cũ, tức là bạn nhận eventual consistency để đổi lấy hiệu năng đọc. Building Microservices tóm tắt CAP: khi có partition mạng, hệ phân tán chỉ giữ được hoặc nhất quán hoặc sẵn sàng.19
CAP chỉ nói về lúc có lỗi mạng. PACELC bổ sung phần còn lại: nếu có partition (P) thì chọn giữa sẵn sàng (A) và nhất quán (C); nếu không (Else) thì vẫn chọn giữa độ trễ thấp (L) và nhất quán (C).20 Áp dụng cho bài này:
- Postgres một primary cộng replica bất đồng bộ: khi bình thường ưu tiên latency (đọc replica có thể cũ), chọn latency hơn consistency cho đường đọc đó.
- NewSQL kiểu đồng thuận: ưu tiên nhất quán cả lúc bình thường, nên trả bằng độ trễ ghi cao hơn. Nếu mạng chia cắt, phía thiểu số của một nhóm đồng thuận không ghi được, tức là chọn C hơn A. Phân loại chính xác từng sản phẩm chưa có sách trong thư viện.21
Hướng dẫn theo quy mô: 10k, 1M, 50M+ DAU
Các mốc dưới đây là quy tắc ước lượng của khoá học, cần load test với workload thật.3
| Quy mô | Đỉnh ước tính | Gợi ý database |
|---|---|---|
| ~10k DAU | Vài chục đến vài trăm QPS | Một Postgres managed có standby, index tốt. Không replica, không shard. |
| ~1M DAU | Vài nghìn QPS đỉnh | Primary + 1 đến 3 read replica, cache Redis cho đọc nóng, partition bảng sự kiện theo tháng, connection pool. Vẫn một primary. |
| 50M+ DAU | Chục nghìn ghi/giây trở lên | Shard theo khoá nghiệp vụ (user, tenant), hoặc NewSQL nếu cần SQL + ACID + đa vùng, hoặc tách phần dữ liệu ghi nặng sang Cassandra hay DynamoDB (xem Chọn NoSQL). Mỗi shard vẫn có replica. |
Ví dụ nhẩm: 1 triệu DAU, mỗi người 30 request mỗi ngày, trong đó 90% đọc. Trung bình 30.000.000 / 86.400 ≈ 350 request/giây, đỉnh ×5 ≈ 1.750. Phần ghi chỉ khoảng 175 ghi/giây ở đỉnh, rất xa trần một primary. Đây là lý do bài Chat ở mốc 1M DAU vẫn chạy được trên Postgres.22
Khi tăng lên 50 triệu DAU, cùng giả định cho ra khoảng 8.700 ghi/giây ở đỉnh. Con số ghi vẫn chấp nhận được với một primary mạnh, nhưng dung lượng dữ liệu tích luỹ và tải đọc mới là thứ kéo bạn sang shard hoặc replica nhiều tầng. Hãy luôn tính ra chiều nào là nút thắt, rồi mới chọn bậc.
Tín hiệu nên lên bậc tiếp theo
| Tín hiệu | Bậc cần cân nhắc |
|---|---|
| Query chậm vì quét bảng, CPU DB cao do vài query | Tune, index (bậc 1) |
| CPU, RAM, I/O cạn nhưng còn máy lớn hơn để mua | Scale up (bậc 2) |
| Tải đọc làm primary quá 60% CPU, cache đã dùng | Read replica (bậc 3) |
| Bảng sự kiện vài trăm GB, xoá dữ liệu cũ chậm | Partitioning (bậc 4) |
| Primary ghi đã gần trần, hoặc dữ liệu vượt vài TB, replica không cứu được | Sharding (bậc 5) |
| Cần ACID đa vùng, đã có nhiều shard thủ công đau đầu | NewSQL (bậc 6) |
Các ngưỡng (60% CPU, vài TB) là quy tắc của khoá học, không phải số cứng.3
Khi nào KHÔNG nên scale ngang
Scale ngang quá sớm là sai lầm đắt nhất vì bạn trả chi phí phức tạp mỗi ngày cho vấn đề chưa xảy ra.
- Dữ liệu còn nhỏ. Grokking nêu rằng database quan hệ hợp khi cần ACID và dữ liệu còn vừa trong một node; NoSQL phù hợp khi dữ liệu lớn.23 Nếu dữ liệu còn dưới vài trăm GB, một Postgres tốt thường đủ.
- Chưa thử các bậc rẻ. Nhiều hệ thống "chậm" chỉ thiếu một index hoặc có một query N+1.
- Cần join và transaction nhiều bảng ở mọi nơi. Shard rồi tự viết saga thường tốn hơn mua máy to.
- Nhóm nhỏ, không có người vận hành. Một cụm shard hay NewSQL tự host cần người hiểu nó. Dịch vụ managed giảm bớt nhưng tốn tiền.
- Chỉ vì "để sau này scale". Hãy chuẩn bị đường đi (chọn key tốt, tránh join không cần thiết) nhưng đừng xây hạ tầng cho 50 triệu người dùng khi bạn có 5.000.
Trong phỏng vấn: Trình bày theo bậc thang. Nói rõ bạn bắt đầu ở bậc nào với con số ước lượng, tín hiệu nào khiến bạn lên bậc kế tiếp, và cái giá của bậc đó (lag, scatter-gather, transaction xuyên shard). Chọn NewSQL khi bài toán cần cả SQL, ACID và đa vùng, và nói thẳng bạn chấp nhận độ trễ ghi cao hơn.
Khi nào nhắc trong phỏng vấn
Các bài dưới đây dùng các bậc của thang này (xem thêm Grokking ch.30 và ch.33, nơi Uber và Instagram kết hợp SQL với Cassandra).2425
- Chat: lịch sử tin nhắn lớn nhanh, chuyển từ Postgres sang wide-column khi ghi vượt sức một primary.
- Đặt vé sự kiện và Thanh toán: cần ACID, nên giữ giao dịch trong một shard hoặc dùng saga.
- News feed và Instagram: đọc nhiều, dùng replica và cache.
- Url shortener: shard theo hash của short code.
- Metrics monitoring và Ad click aggregator: ghi rất nhiều, ít cần join.
Nguồn trích dẫn
-
Grokking Modern System Design · ch.9 Databases · bài "Data Replication" và "Data Partitioning", đoạn mở đầu về lý do chia dữ liệu (đọc, ghi, dung lượng). ↩
-
Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · mục "Standard numbers to remember" (bảng Important Rates). ↩
-
Quy tắc ước lượng của khoá học, xem bài Chọn server. Không phải số liệu trích từ sách; cần load test với ứng dụng thật. ↩ ↩2 ↩3 ↩4
-
Grokking Modern System Design · ch.9 Databases · bài "Data Replication", mục "Single leader/primary-secondary replication". ↩ ↩2 ↩3
-
Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục về read replicas để scale đọc (tr. 222–223). ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Data Replication", mục "Synchronous versus asynchronous replication". ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Data Replication", danh sách các khó khăn của replication (replication lag khi bất đồng bộ). ↩
-
Chưa có nguồn trong thư viện: read-your-writes và cách đạt nó bằng đọc từ primary sau khi ghi không có trong các sách của thư viện; chỉ nêu khái niệm. ↩
-
Chưa có nguồn trong thư viện: thư viện không có sách về partitioning của PostgreSQL hay MySQL; phần này chỉ nêu khái niệm cơ bản. ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Data Partitioning", mục "Sharding", "Horizontal sharding", "Key-range based sharding", "Hash-based sharding". ↩ ↩2
-
MongoDB: The Definitive Guide 2e (file .zip, tải về giải nén) · ch.13 Introduction to Sharding · mục "Introduction to Sharding" và "A One-Minute Test Setup" (tr. 236–240). ↩ ↩2
-
Chưa có nguồn trong thư viện: thư viện không có sách về Vitess hay Citus; chỉ nêu tên như ví dụ của kiểu middleware và extension. ↩
-
MongoDB: The Definitive Guide 2e (file .zip, tải về giải nén) · ch.15 Choosing a Shard Key · mục "Taking Stock of Your Usage" (tr. 257–258). ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Data Partitioning", mục "Consistent hashing" và "Rebalance the partitions". ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Types of Databases", ô Note cuối mục "Choose the right database" (Google Cloud Spanner). ↩ ↩2
-
Chưa có nguồn trong thư viện: thư viện không có sách về Spanner, CockroachDB, TiDB, YugabyteDB hay giao thức đồng thuận theo range; phần này chỉ nêu khái niệm, không có số liệu, phiên bản hay giá. ↩ ↩2
-
Building Microservices (file .zip, tải về giải nén) · ch.5 Splitting the Monolith · mục "Distributed Transactions" (tr. 92). ↩
-
Microservices Patterns (file .zip, tải về giải nén) · ch.4 Managing transactions with sagas · §4.1.2 The trouble with distributed transactions và §4.1.3 Using the Saga pattern to maintain data consistency. ↩ ↩2
-
Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "CAP Theorem" (tr. 232–234). ↩
-
Lý thuyết · PACELC (Daniel Abadi đề xuất): nếu có phân vùng mạng (P) thì phải chọn giữa sẵn sàng (A) và nhất quán (C); nếu không (Else) thì vẫn phải chọn giữa độ trễ thấp (L) và nhất quán (C), vì đồng bộ các bản sao tốn thời gian. PA/EL nghĩa là ưu tiên sẵn sàng khi có phân vùng và độ trễ khi bình thường; PC/EC nghĩa là luôn ưu tiên nhất quán. Ví dụ số: replica bất đồng bộ trả lời trong 1 ms nhưng có thể cũ vài trăm ms; đọc từ leader sau khi chờ đa số xác nhận có thể mất 30 ms nếu replica ở xa nhưng luôn mới. Đó là L đổi lấy C. ↩
-
Chưa có nguồn trong thư viện: phân loại PACELC chính xác của từng sản phẩm NewSQL chưa có sách tương ứng. ↩
-
Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · mục "Requests estimation". ↩
-
Grokking Modern System Design · ch.9 Databases · bài "Types of Databases", mục "Choose the right database" (bảng Relational và Non-relational). ↩
-
Grokking Modern System Design · ch.30 Uber · phần chọn Cassandra cho vị trí tài xế và chuyến đi (yêu cầu sẵn sàng cao, dữ liệu lớn). ↩
-
Grokking Modern System Design · ch.33 Instagram · ô Note "Most modern services use both SQL and NoSQL stores". ↩