Harbor

Chọn server: bao nhiêu máy, bao nhiêu RAM

Cách quyết định loại máy, số instance và RAM cho từng thành phần (Node.js, PostgreSQL, Redis, Elasticsearch), có ví dụ tính ở 10 nghìn, 1 triệu và 50 triệu DAU và cách load test để kiểm chứng.

15 phút đọcHạ tầng theo quy mô

Trong phỏng vấn, câu "mình sẽ dùng vài server" không đủ. Người phỏng vấn muốn thấy bạn biết mỗi máy chịu được bao nhiêu, cần bao nhiêu máy, và vì sao chọn cấu hình đó. Bài này đưa ra một quy trình ba bước: xác định loại tải, chọn loại máy, rồi tính số máy và RAM. Mọi con số dưới đây là ước lượng để tính nhẩm. Hệ thống thật cần load test.1

Các con số năng lực riêng của khoá (khoảng 500 req/s mỗi vCPU, 1.000 req/s cho máy 2 vCPU, chạy tối đa 60% CPU, hệ số Redis ×1,3…) là quy tắc ước lượng của khoá học, không phải số trích từ sách. Những bài khác trong khoá trích về đây khi dùng các quy tắc này.2

Tải thuộc loại gì

Bước đầu tiên là hỏi: phần việc này bị giới hạn bởi tài nguyên nào. Câu trả lời quyết định bạn mua thêm CPU, thêm RAM hay thêm máy. Grokking ch.5 chia request thành hai loại theo tài nguyên trở thành nút thắt: CPU-bound và memory-bound.3 Khoá học thêm loại I/O-bound để mô tả API phần lớn thời gian chỉ chờ.

Loại tảiDấu hiệuVí dụTài nguyên quyết định
I/O-boundCPU thấp, latency chủ yếu là chờ DB, cache, API khácREST API, BFF, gateway, WebSocketSố kết nối, connection pool, latency phía sau
CPU-boundCPU gần 100% dù ít requestEncode video, resize ảnh, nén, tính điểm xếp hạng, hash mật khẩuSố core
Memory-boundHiệu năng tụt mạnh khi dữ liệu nóng không vừa RAMPostgreSQL, Redis, ElasticsearchDung lượng RAM so với working set

Phần lớn API viết bằng Node.js là I/O-bound: khi gọi một thao tác bất đồng bộ, stack trả quyền về event loop, nên process vẫn rảnh để nhận request khác trong lúc chờ.4 Theo giả định của khoá học, một request điển hình mất 50–100 ms nhưng CPU chỉ làm việc vài ms, thời gian còn lại là chờ database. Node.js Design Patterns ghi rằng một thread Node.js dùng tốt non-blocking I/O thường xử lý được "vài trăm" request mỗi giây, tuỳ ứng dụng.5 Khoá học chốt con số tròn khoảng 500 req/s cho một vCPU với API đơn giản (quy tắc ước lượng của khoá học).2

Grokking ch.5 tách rõ hai công thức: RPS cho tải CPU-bound bằng số CPU (hardware thread) nhân 1 ÷ thời gian mỗi tác vụ; RPS cho tải memory-bound bằng dung lượng RAM ÷ bộ nhớ mỗi worker, nhân 1 ÷ thời gian mỗi tác vụ. Ví dụ trong sách: 72 thread, 200 ms mỗi tác vụ CPU-bound cho 360 RPS; 240 GB RAM, 300 MB mỗi worker, 50 ms mỗi tác vụ cho 16.000 RPS. Ý tưởng chung: tìm tài nguyên cạn trước, rồi chia.3 Sách cũng nêu hai đầu của dải: máy chỉ phục vụ nội dung tĩnh từ RAM có thể đạt tới 500 nghìn RPS, còn tác vụ nặng tính toán như xử lý ảnh có thể chỉ được tối đa 50 RPS.1

Sai lầm hay gặp: Đặt việc CPU-bound (resize ảnh, sinh PDF) chung process với API. Một tác vụ đồng bộ dài không trả quyền về event loop, nên mọi request khác đều phải chờ.4 Tách ra worker riêng đọc từ queue, xem Tác vụ dài.6

Chọn loại máy theo tỉ lệ vCPU : RAM

Grokking ch.5 mô tả trung tâm dữ liệu dùng nhiều loại server cho các tải khác nhau: web server cần nhiều tài nguyên tính toán, application server có thể cần nhiều CPU và bộ nhớ, storage server cần dung lượng lưu trữ lớn.7 Trên cloud, ý tưởng đó thể hiện qua các "family" máy chia theo tỉ lệ CPU và RAM. Bảng dưới là phân loại khoá học dùng để tính nhẩm (giả định của khoá học, cấu hình cụ thể khác nhau giữa các nhà cung cấp).8

FamilyTỉ lệ vCPU : RAMVí dụ cấu hìnhDùng cho
General purpose1 : 4 GB2 vCPU · 8 GBAPI, web server, worker thường
Compute optimized1 : 2 GB8 vCPU · 16 GBEncode video, xử lý ảnh, tính toán nặng
Memory optimized1 : 8 GB4 vCPU · 32 GBPostgreSQL, Redis, Elasticsearch
Storage optimized1 : 8 GB + NVMe gắn trực tiếp8 vCPU · 64 GB + NVMeKafka, Cassandra tự vận hành
BurstableNhỏ, CPU có hạn mức2 vCPU · 4 GBMôi trường dev, staging, tải thấp

Khoá này dùng 2 vCPU · 4 GB làm đơn vị API chuẩn (quy tắc ước lượng của khoá học).2 Đây là máy nhỏ, rẻ, đủ cho 2 process Node.js, và dễ nhân bản. Lý do chọn máy nhỏ cho API: khi một máy chết, bạn mất một phần nhỏ năng lực; Building Microservices cũng lưu ý một máy to có thể đắt hơn hai máy nhỏ có cùng tổng năng lực, và chỉ có một máy thì không tăng khả năng chịu lỗi.9 Với database thì ngược lại: khi ghi chạm giới hạn, mua máy to hơn thường là cách nhanh nhất, vì chia dữ liệu (sharding) phức tạp hơn nhiều.10

Mẹo: Máy burstable rất rẻ nhưng hết "credit" CPU thì bị bóp hiệu năng. Đừng dùng cho production có tải đều.8

Công thức số instance

Công thức khoá dùng xuyên suốt (quy tắc ước lượng của khoá học):2

avg  = DAU × request mỗi người mỗi ngày ÷ 86.400
peak = avg × hệ số đỉnh            (thường ×3 đến ×10)
N    = max(2, ceil(peak ÷ (năng lực một máy × 0,6)))

Dòng đầu giống cách Grokking ch.5 tính RPS trung bình: DAU nhân số request mỗi người rồi chia 86.400 giây. Sách cũng nhắc rằng con số này dễ ước lượng thiếu, vì một request thật đi qua nhiều tầng server, nên ở bước sau sách dùng giả định rất thận trọng là mọi request trong ngày đến cùng một giây.11 Hệ số đỉnh ×3 đến ×10 của khoá là một cách trung gian, cần thay bằng số đo thật khi có.

Với máy 2 vCPU · 4 GB, năng lực ước lượng 1.000 req/s, nên mẫu số là 600. Ba ý nằm trong công thức:

  • × 0,6 (headroom 40%): mỗi máy chỉ chạy tối đa khoảng 60% CPU lúc đỉnh (quy tắc của khoá).2 Phần còn lại hấp thụ đỉnh bất ngờ, GC pause, và thời gian autoscaling cần để bật máy mới. Building Microservices nêu đúng lý do này: nếu chỉ được báo trước vài phút khi tải tăng mà bật thêm máy mất lâu hơn, bạn phải giữ sẵn năng lực dư để lấp khoảng trống; và thừa năng lực thường tốt hơn thiếu.12
  • Tối thiểu 2: chỉ một máy là một điểm chết (single point of failure). Cách đơn giản nhất để tránh là chạy nhiều instance sau load balancer.13
  • N+1: sau khi tính N, cộng thêm một máy dự phòng nếu N nhỏ. Khi N lớn và chạy nhiều zone, tính theo zone: mất một zone thì các zone còn lại vẫn phải gánh được đỉnh. Building Microservices khuyên trải dịch vụ qua nhiều availability zone vì nhà cung cấp không bảo đảm một node, thậm chí một zone, luôn sống.14

Định luật Little giúp kiểm tra chéo: số request đang xử lý cùng lúc L = λ × W.15 Đỉnh 1.736 req/s, mỗi request 60 ms thì L ≈ 104 request đồng thời. Con số này cho biết cần pool kết nối DB cỡ nào.

RAM cho từng thành phần

Node.js

  • Một process Node.js thường dùng 100–300 MB khi chạy API thông thường (ước lượng của khoá học; ví dụ trong Grokking ch.5 cũng giả định mỗi worker dùng 300 MB).3
  • Mỗi process Node.js chạy một thread, nên để dùng hết các core trên một máy, chạy nhiều process bằng module cluster (thường một worker mỗi CPU), PM2, hoặc để orchestrator chạy nhiều container 1 vCPU. Mỗi worker là một process riêng với event loop và bộ nhớ riêng.16
  • Node.js có giới hạn heap mặc định và chỉnh bằng tuỳ chọn --max-old-space-size.5 Khoá học khuyên đặt giá trị này thấp hơn giới hạn RAM của container, để process tự GC trước khi bị OOM kill.
  • Máy 2 vCPU · 4 GB chạy 2 process, mỗi process giới hạn khoảng 1,5 GB heap, còn dư cho hệ điều hành (ước lượng của khoá học).2

PostgreSQL

  • Working set: tổng dữ liệu nóng và index được truy cập thường xuyên. Mục tiêu là working set vừa RAM; khi đó DB chỉ phải đọc đĩa cho số ít request "bất thường". Ví dụ trong sách MongoDB: 500 GB dữ liệu nhưng working set chỉ 40 GB, máy 50 GB RAM là đủ giữ working set. Khi working set không vừa RAM, dữ liệu bị đẩy ra vào đĩa liên tục và hiệu năng không còn dự đoán được.17 Nguyên tắc này áp dụng cho mọi DB dùng bộ nhớ đệm, kể cả PostgreSQL.
  • shared_buffers: khoảng 25% RAM. Phần lớn RAM còn lại để page cache của hệ điều hành lo.18
  • work_mem: cấp cho mỗi thao tác sort hoặc hash, mỗi kết nối có thể dùng nhiều lần. Đặt quá cao với nhiều kết nối dễ hết RAM.18
  • Kết nối: mỗi kết nối là một process, tốn vài MB. 30 instance API × pool 10 = 300 kết nối là quá nhiều. Đặt PgBouncer chế độ transaction phía trước để gom xuống 50–100 kết nối thật.18
; Postgres 4 vCPU · 16 GB (ước lượng khởi đầu)
shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 16MB
max_connections = 200   ; PgBouncer đứng trước

Chi tiết hơn về index và truy vấn ở bài PostgreSQL và Database index.

Redis

Redis giữ toàn bộ dữ liệu trong RAM nên phải tính kỹ. Redis in Action cho thấy overhead mỗi phần tử có thể lớn hơn chính dữ liệu (ví dụ một LIST thường tốn khoảng 21 byte overhead để lưu 3 byte dữ liệu trên nền 32-bit), vì vậy cần cộng phần overhead khi tính.19 Công thức của khoá (quy tắc ước lượng của khoá học):2

RAM = số item × (key + value + ~100 B overhead) × 1,3

Hệ số 1,3 tính cho phân mảnh bộ nhớ và buffer replication. Ví dụ 5 triệu item × 1 KB: 5.000.000 × 1.100 × 1,3 ≈ 7,2 GB, chọn node 8 GB trở lên. Một node Redis xử lý khoảng 100.000 thao tác/s trên phần cứng phổ thông (tới khoảng 225.000 trên phần cứng mạnh), khớp với dải 100 nghìn đến 1 triệu QPS cho cache server trong Grokking ch.5.2021 Khi đã dùng hết máy lớn nhất muốn trả tiền, hoặc ghi chạm giới hạn một máy, chia dữ liệu ra nhiều node (sharding); khoá học dùng mốc RAM 25–50 GB một node làm ngưỡng cân nhắc Redis Cluster.22 Xem thêm Redis.

Elasticsearch

  • Node chuẩn: 8 vCPU · 32 GB.
  • Heap JVM tối đa khoảng 50% RAM và không vượt khoảng 31 GB. Phần RAM còn lại cho page cache đọc file index.
  • Shard nên nằm trong khoảng 10–50 GB. Index 300 GB thì khoảng 6–10 primary shard.23

Lưu ý: Với database và search, RAM thường hết trước CPU. Theo dõi tỉ lệ cache hit của buffer và số lần đọc đĩa (page fault), đừng chỉ nhìn CPU.17

Scale dọc hay scale ngang

Tiêu chíScale dọc (máy to hơn)Scale ngang (thêm máy)
Thay đổi codeKhôngCần app stateless
Giới hạnMáy lớn nhất của nhà cung cấpGần như không giới hạn
Khả năng chịu lỗiMột máy là một điểm chếtMất một máy chỉ mất một phần
Chi phíMáy to giá tăng nhanh hơn tuyến tínhTuyến tính, cộng chi phí load balancer
Hợp vớiDatabase ở mức 1 và 2API, worker, gateway

Grokking ch.4 mô tả sự đánh đổi này: scale dọc (thêm CPU, RAM cho máy hiện có) chỉ tăng được tới giới hạn của máy và thường đắt; scale ngang dùng máy phổ thông rẻ hơn, nhưng phải xây hệ để nhiều node cùng làm việc như một máy lớn.24 Building Microservices ch.11 liệt kê các cách scale: "go bigger" (máy to hơn, thường là cách thắng nhanh nhưng đắt và không tăng khả năng chịu lỗi nếu chỉ có một máy), tách khối việc (splitting workloads) sang máy hoặc dịch vụ riêng, và trải rủi ro ra nhiều máy, nhiều rack, nhiều zone.914

Để scale ngang API dễ dàng, giữ API stateless ngay từ đầu. Node.js Design Patterns gọi đây là điều kiện tiên quyết: mỗi instance không được giữ thông tin chung trên tài nguyên không chia sẻ được như bộ nhớ hay đĩa; ví dụ session phải nằm ở nơi dùng chung thay vì trong bộ nhớ process.5

  • Session để ở Redis hoặc dùng JWT.
  • File upload để thẳng lên object storage, xem File lớn.
  • Job nền chạy qua queue, không giữ trong bộ nhớ process.6

Ví dụ tính ở ba quy mô

Giả định chung (giả định của khoá học): mỗi người 30 request/ngày, đỉnh ×5, 10% ghi, cache hit 85%. Năng lực máy và các hệ số là quy tắc ước lượng của khoá học.2

Đang vẽ sơ đồ…
10 nghìn DAU1 triệu DAU50 triệu DAU
Request trung bình≈ 3,5 req/s≈ 347 req/s≈ 17.361 req/s
Đỉnh≈ 17 req/s≈ 1.736 req/s≈ 86.806 req/s
N theo công thức2 (mức tối thiểu)ceil(1.736 ÷ 600) = 3ceil(86.806 ÷ 600) = 145
API sau dự phòng2 × 2 vCPU · 4 GB4 × 2 vCPU · 4 GB (N+1)~165 × 2 vCPU · 4 GB trên 3 zone
Tải DB sau cache≈ 4 q/s≈ 408 q/s≈ 20.400 q/s
Database2 vCPU · 8 GB, một instance4 vCPU · 16 GB, primary + standbyNhiều node: shard hoặc DB phân tán
Redis (2 triệu item × 1 KB)Có thể chưa cần≈ 2,9 GB, node 4 GBRedis Cluster, đỉnh vượt 80.000 ops/s

Để so sánh: Grokking ch.5 coi một MySQL đơn lẻ xử lý khoảng 1.000 QPS và key-value store khoảng 10.000 QPS.21 Vì vậy 408 q/s vẫn trong tầm một instance, còn 20.400 q/s thì vượt xa một node quan hệ và phải chia dữ liệu. Primary + standby giữ dữ liệu an toàn khi primary chết, nhưng cần cơ chế đưa standby lên thì DB mới sẵn sàng trở lại.25

Cách ra con số 165 ở 50 triệu DAU: chạy 3 zone, nếu mất một zone thì 2 zone còn lại phải gánh đỉnh ở mức tối đa 80% CPU (quy tắc của khoá). Cần 86.806 ÷ 800 ≈ 109 máy trong 2 zone, tức khoảng 55 máy mỗi zone, tổng 165. Lúc bình thường mỗi máy chạy khoảng 53%. Ở quy mô này nhiều đội chuyển sang máy lớn hơn (8 vCPU · 16 GB) để giảm số node cần quản lý, khoảng 42 máy cho cùng năng lực.214

Bảng đầy đủ về chi phí và kiến trúc từng mức ở bài Hạ tầng theo quy mô. Muốn đổi giả định, dùng Máy tính hạ tầng.

Trong phỏng vấn: Nói rõ giả định trước khi tính ("mình giả sử 30 request mỗi người, đỉnh gấp 5"). Grokking ch.5 cũng nhấn mạnh rằng ở mức thiết kế, ước lượng thô là phù hợp vì mục đích là tìm cận trên hợp lý của tài nguyên.11

Load test để kiểm chứng

Con số 1.000 req/s cho máy 2 vCPU là điểm xuất phát (quy tắc ước lượng của khoá học).2 API của bạn có thể nhanh hơn nhiều (đọc cache) hoặc chậm hơn nhiều (truy vấn nặng, JSON lớn). Grokking ch.5 nhắc rằng hệ thống thật cải thiện ước lượng ban đầu dần dần bằng tính nhẩm, mô phỏng, dựng mẫu và giám sát.11 Trước khi chốt cấu hình, đo trên một instance với dữ liệu gần thật.

Quy trình (dựa trên mục Performance Tests của Building Microservices: tăng dần số người dùng giả lập để xem latency thay đổi theo tải, môi trường và khối lượng dữ liệu càng giống production càng tốt, và phải có ngưỡng mục tiêu để biết kết quả đạt hay không):26

  1. Dựng một instance API cùng cấu hình production, DB có dữ liệu cỡ thật.
  2. Tăng dần tải theo bậc, ghi lại p95, p99 latency, tỉ lệ lỗi, CPU, RAM.
  3. Năng lực một máy là mức req/s cao nhất mà p95 vẫn trong mục tiêu (ví dụ dưới 200 ms) và lỗi dưới 0,1%.
  4. Nhân 0,6 rồi đưa vào công thức N.

Ví dụ với k6, dùng arrival rate để giữ số request mỗi giây cố định bất kể latency:

import http from "k6/http";
import { check } from "k6";

export const options = {
  scenarios: {
    ramp: {
      executor: "ramping-arrival-rate",
      startRate: 100,
      timeUnit: "1s",
      preAllocatedVUs: 200,
      stages: [
        { target: 500, duration: "2m" },
        { target: 1000, duration: "2m" },
        { target: 1500, duration: "2m" },
      ],
    },
  },
  thresholds: {
    http_req_duration: ["p(95)<200"],
    http_req_failed: ["rate<0.001"],
  },
};

export default function () {
  const res = http.get("http://api.internal/products?page=1");
  check(res, { "status 200": (r) => r.status === 200 });
}

Với một endpoint đơn lẻ, autocannon nhanh gọn hơn (100 kết nối trong 30 giây, xem req/s và latency p99):27

npx autocannon -c 100 -d 30 http://localhost:3000/products?page=1

Sai lầm hay gặp: Load test từ chính máy chạy API, hoặc với DB rỗng. Kết quả đẹp nhưng vô nghĩa. Chạy công cụ tải từ máy khác cùng region và dùng dữ liệu cỡ production.26

Nguồn trích dẫn

  1. Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · bài "Put Back-of-the-envelope Numbers in Perspective", mục "Requests estimation" (đoạn cuối: 500k RPS cho nội dung tĩnh, 50 RPS cho xử lý ảnh; capacity estimation là bài toán khó). ↩ ↩2

  2. Quy tắc ước lượng của khoá học, định nghĩa trong chính bài này. 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 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10

  3. Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · bài "Put Back-of-the-envelope Numbers in Perspective", mục "Requests estimation" (công thức RPS CPU-bound và memory-bound, giả định 300 MB mỗi worker). ↩ ↩2 ↩3

  4. Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.9 Advanced Asynchronous Recipes · mục "Running CPU-bound tasks" (tr. 371). ↩ ↩2

  5. Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.10 Scalability and Architectural Patterns · mục "Scaling Node.js applications" (tr. 388) và "Cloning and load balancing" (tr. 391). ↩ ↩2 ↩3

  6. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Worker-Based Systems" (tr. 220–221). ↩ ↩2

  7. Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · bài "Put Back-of-the-envelope Numbers in Perspective", mục "Types of data center servers". ↩

  8. Chưa có nguồn trong thư viện: các "family" máy và tỉ lệ vCPU : RAM là cách đặt tên của nhà cung cấp cloud, sách trong thư viện không mô tả; bảng chỉ dùng làm giả định để tính nhẩm. ↩ ↩2

  9. Building Microservices (Sam Newman) (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Scaling" › "Go Bigger" và "Splitting Workloads" (tr. 217). ↩ ↩2

  10. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Scaling Databases" › "Scaling for Writes" (tr. 223–224). ↩

  11. Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · bài "Examples of Resource Estimation", mục "Number of servers required". ↩ ↩2 ↩3

  12. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Autoscaling" (tr. 230–231). ↩

  13. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Load Balancing" (tr. 219–220). ↩

  14. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Spreading Your Risk" (tr. 217–218). ↩ ↩2 ↩3

  15. Lý thuyết · Định luật Little: trong hệ thống ổn định, số yêu cầu đang xử lý trung bình L bằng tốc độ đến λ nhân thời gian ở lại trung bình W, tức L = λ × W. Ví dụ λ = 470 req/s và W = 0,08 s thì L = 470 × 0,08 ≈ 38 request cùng lúc; mỗi instance xử lý được c request đồng thời thì cần ít nhất L ÷ c instance, rồi chia thêm cho 0,6 để chừa chỗ cho đỉnh. Định luật đúng cho mọi hàng đợi ổn định, không phụ thuộc phân phối thời gian đến hay thời gian xử lý, nên dùng được để kiểm tra chéo kết quả ước lượng. ↩

  16. Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.10 Scalability and Architectural Patterns · mục "The cluster module" và "Scaling with the cluster module" (tr. 392–395). ↩

  17. MongoDB: The Definitive Guide 2e (file .zip, tải về giải nén) · ch.21 Monitoring MongoDB · mục "Calculating the Working Set" và "Some Working Set Examples" (tr. 348–350). ↩ ↩2

  18. Chưa có nguồn trong thư viện: thư viện không có sách vận hành PostgreSQL; các tham số này là điểm khởi đầu thường dùng, cần chỉnh theo đo đạc. ↩ ↩2 ↩3

  19. Redis in Action · ch.9 Reducing memory use · §9.1.1 The ziplist representation (tr. 210–211). ↩

  20. Redis in Action · ch.6 Application components in Redis · §6.2 Distributed locking › 6.2.2 Simple locks (tr. 120–121; con số 100.000 thao tác/s, tới 225.000 trên phần cứng cao cấp). ↩

  21. Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · bài "Put Back-of-the-envelope Numbers in Perspective", mục "Standard numbers to remember", bảng "Important Rates". ↩ ↩2

  22. Redis in Action · ch.10 Scaling Redis · §10.2 Scaling writes and memory capacity (tr. 232–233). ↩

  23. Chưa có nguồn trong thư viện: thư viện không có sách về Elasticsearch; các mốc heap và kích thước shard là ước lượng khởi đầu, cần kiểm chứng bằng load test. ↩

  24. Grokking Modern System Design · ch.4 Non-functional System Characteristics · bài "Scalability", mục "Vertical scalability--scaling up" và "Horizontal scalability--scaling out". ↩

  25. Building Microservices (file .zip, tải về giải nén) · ch.11 Microservices at Scale · mục "Scaling Databases" › "Availability of Service Versus Durability of Data" (tr. 222). ↩

  26. Building Microservices (file .zip, tải về giải nén) · ch.7 Testing · mục "Performance Tests" (tr. 152–153). ↩ ↩2

  27. Chưa có nguồn trong thư viện: k6 và autocannon là công cụ cụ thể, sách trong thư viện không đề cập; chỉ dùng làm ví dụ minh hoạ quy trình. ↩