Tổng quan nhanh
Tổng hợp từ Designing Data-Intensive Applications (Martin Kleppmann, 2017). Thuật ngữ giữ tiếng Anh để khớp với sách và phỏng vấn.
Bức tranh tổng thể
Cuốn sách chia 3 phần, đi từ 1 máy → nhiều máy → nhiều hệ thống ghép lại:
| Phần | Câu hỏi trung tâm | Chương |
|---|---|---|
| I. Foundations | Dữ liệu được mô hình hoá, lưu trữ, mã hoá thế nào trên một node? | 1–4 |
| II. Distributed Data | Chuyện gì xảy ra khi dữ liệu nằm trên nhiều node? | 5–9 |
| III. Derived Data | Làm sao ghép nhiều hệ thống (DB, cache, index, warehouse) cho nhất quán? | 10–12 |
Thông điệp xuyên suốt: không có công cụ "tốt nhất", chỉ có trade-off. Kỹ sư giỏi là người biết mỗi lựa chọn đánh đổi cái gì (latency ↔ consistency, write ↔ read, đơn giản ↔ linh hoạt).
Top 15 ý phải nhớ (cheat sheet)
- Fault ≠ failure: fault là một thành phần lệch spec; failure là cả hệ thống ngừng phục vụ. Mục tiêu là fault-tolerant, không phải fault-free.
- Đo performance bằng percentile (p50, p95, p99), không dùng average. Tail latency quyết định trải nghiệm của user quan trọng nhất (thường là user nhiều dữ liệu nhất).
- Document model hợp với dữ liệu dạng cây, one-to-many, locality cao; relational hợp với many-to-many và join; graph hợp khi mọi thứ liên kết với mọi thứ.
- LSM-tree (log-structured, SSTable + compaction): write nhanh. B-tree (update in-place, page 4KB): read ổn định, phổ biến nhất.
- OLTP (row-oriented, truy cập theo key) khác hẳn OLAP (column-oriented, scan + aggregate nhiều hàng).
- Schema evolution cần backward + forward compatibility; Protobuf/Thrift/Avro giải quyết bằng field tag / schema resolution.
- Replication: single-leader (đơn giản), multi-leader (multi-datacenter, offline), leaderless (quorum
w + r > n). - Replication lag gây ra các anomaly: cần read-your-writes, monotonic reads, consistent prefix reads.
- Partitioning: by key range (range query tốt, dễ hot spot) vs by hash (phân tán đều, mất range query). Không dùng
hash mod Nđể rebalance. - ACID: "C" là thuộc tính của application, không phải DB. "I" trong thực tế thường là isolation yếu hơn serializable.
- Isolation levels và anomaly: read committed → snapshot isolation (MVCC) → serializable (serial execution / 2PL / SSI). Write skew chỉ serializable mới chặn được.
- Trong distributed system: network không đáng tin, clock không đáng tin, process có thể bị pause bất kỳ lúc nào → dùng timeout, fencing token, quorum.
- Linearizability (recency guarantee) ≠ serializability (isolation của transaction). CAP thực chất là: khi network partition, chọn consistency (linearizable) hay availability.
- Consensus (Raft, Paxos, ZAB) ≈ total order broadcast ≈ linearizable CAS. 2PC là atomic commit nhưng blocking khi coordinator chết.
- Log là trung tâm: event log (Kafka) + change data capture + derived views (cache, search index, warehouse) — "unbundling the database".
PART I — Foundations of Data Systems
Chương 1 — Reliable, Scalable, Maintainable
Ba mục tiêu
- Reliability: hệ thống vẫn chạy đúng khi có fault.
- Hardware faults: đĩa hỏng (MTTF ~10–50 năm → cluster 10.000 đĩa thì mỗi ngày hỏng 1). Giải bằng redundancy, và ngày nay bằng phần mềm chịu lỗi cả node.
- Software errors: bug có tính hệ thống, xảy ra đồng loạt trên mọi node (leap second, cascading failure). Khó hơn hardware fault.
- Human errors: nguyên nhân hàng đầu gây outage. Giảm bằng: API khó dùng sai, sandbox, test kỹ, rollback nhanh, monitoring (telemetry).
- Scalability: khả năng đối phó khi load tăng.
- Mô tả load bằng load parameters (req/s, tỉ lệ read/write, số user online, cache hit rate...).
- Ví dụ Twitter home timeline: fan-out on read (join lúc đọc) vs fan-out on write (đẩy tweet vào cache timeline của từng follower). Celebrity có hàng triệu follower → dùng hybrid.
- Mô tả performance: throughput (batch) hoặc response time (online). Dùng percentiles; p99/p999 = tail latency. Tail latency amplification: 1 request gọi nhiều backend → chỉ cần 1 cái chậm là cả request chậm.
- SLO/SLA thường định nghĩa theo percentile.
- Scale up (vertical) vs scale out (horizontal, shared-nothing). Không có kiến trúc "magic scaling sauce" — phụ thuộc vào load pattern.
- Maintainability:
- Operability: dễ vận hành (monitoring, automation, docs, hành vi dự đoán được).
- Simplicity: loại bỏ accidental complexity bằng abstraction tốt.
- Evolvability: dễ thay đổi (agile, TDD, refactoring ở mức hệ thống).
Cần nhớ
- Response time ≠ latency: latency là thời gian chờ được xử lý; response time là tổng thời gian client thấy.
- Khi load test, client phải gửi request độc lập, không chờ response trước (tránh che mất queueing delay).
Chương 2 — Data Models & Query Languages
Relational vs Document vs Graph
| Relational | Document | Graph | |
|---|---|---|---|
| Hợp với | many-to-one, many-to-many, join | cây one-to-many, load cả document | quan hệ phức tạp, đệ quy |
| Schema | schema-on-write | schema-on-read | linh hoạt |
| Ưu | join, normalization | locality, gần object trong code | traversal tự nhiên |
| Nhược | object-relational mismatch | join yếu, update document lớn tốn | ít chuẩn hoá công cụ |
- Object-relational impedance mismatch: object trong code ≠ bảng → ORM chỉ giảm bớt, không loại bỏ.
- Normalization: dùng ID thay vì lặp chuỗi → tránh inconsistency, nhưng cần join. Denormalize = nhanh đọc, khó ghi.
- Lịch sử lặp lại: hierarchical model (IMS) ↔ network model (CODASYL) ↔ relational — document DB đang gặp lại vấn đề many-to-many của IMS.
- Xu hướng hội tụ: PostgreSQL/MySQL hỗ trợ JSON; MongoDB/RethinkDB hỗ trợ join.
Query languages
- Declarative (SQL, CSS) > imperative: DB tự tối ưu, dễ song song hoá.
- MapReduce là giữa hai loại: code tự viết nhưng mô hình rõ ràng.
- Graph: property graph (Neo4j, Cypher), triple-store (RDF, SPARQL), Datalog. Truy vấn đệ quy trong SQL dùng
WITH RECURSIVE— dài và khó hơn nhiều.
Chương 3 — Storage & Retrieval ⭐ (rất hay hỏi)
Index = đánh đổi: tăng tốc read, làm chậm write
- Hash index (Bitcask): hash map trong RAM trỏ tới offset trong log file append-only. Nhanh nhưng key phải vừa RAM, không range query.
- Log được chia segment, chạy compaction + merge để bỏ bản ghi cũ; xoá dùng tombstone.
- SSTable + LSM-tree (LevelDB, RocksDB, Cassandra, HBase):
- Write vào memtable (cây cân bằng trong RAM) → đầy thì flush ra đĩa thành SSTable (sorted by key).
- Read: memtable → SSTable mới nhất → cũ hơn. Dùng Bloom filter để tránh đọc SSTable không chứa key.
- Crash recovery: WAL cho memtable.
- Compaction: size-tiered vs leveled.
- B-tree (hầu hết RDBMS):
- Page cố định (~4KB), cây branching factor vài trăm, độ sâu O(log n) — 4 tầng đủ chứa 256TB.
- Update in-place → cần WAL (redo log) để chống crash; concurrency dùng latch.
- Tối ưu: copy-on-write (LMDB), key abbreviation, sibling pointers.
- So sánh LSM vs B-tree:
- LSM: write throughput cao hơn (sequential write, write amplification thấp hơn), nén tốt hơn. Nhược: compaction ảnh hưởng tail latency, có thể không theo kịp write.
- B-tree: mỗi key chỉ ở một chỗ → phù hợp transaction isolation (lock trên range key), read dự đoán được.
- Index khác: clustered index (lưu row trong index), covering index, multi-column / concatenated index, R-tree (geo), full-text / fuzzy index (Lucene), in-memory DB (Redis, VoltDB — nhanh vì không cần encode dữ liệu cho đĩa, không phải vì không đọc đĩa).
OLTP vs OLAP
| OLTP | OLAP | |
|---|---|---|
| Đọc | ít record, theo key | scan + aggregate hàng triệu record |
| Ghi | random, low latency | bulk import (ETL) hoặc stream |
| Người dùng | end user qua app | analyst |
| Bottleneck | disk seek | disk bandwidth |
- Data warehouse + ETL; schema star (fact table + dimension tables) / snowflake.
- Column-oriented storage: lưu từng cột riêng → chỉ đọc cột cần; nén tốt (bitmap encoding, run-length); vectorized processing. Sort order giúp nén và query; ghi dùng cách giống LSM.
- Materialized view / data cube: precompute aggregate để query nhanh.
Chương 4 — Encoding & Evolution
- Rolling upgrade → code cũ và mới cùng tồn tại → cần:
- Backward compatibility: code mới đọc được data cũ.
- Forward compatibility: code cũ đọc được data mới (bỏ qua field lạ, không làm mất field đó khi ghi lại).
- Format:
- Language-specific (Java Serializable, pickle): tránh — gắn với 1 ngôn ngữ, lỗ hổng bảo mật, versioning kém.
- JSON/XML/CSV: dễ đọc nhưng mơ hồ về số (số > 2^53 trong JS), không có binary string, schema tuỳ chọn.
- Thrift / Protocol Buffers: dùng field tag (số). Thêm field mới phải optional hoặc có default; không bao giờ dùng lại tag đã xoá.
- Avro: không có tag; writer's schema và reader's schema được so khớp theo tên field. Hợp với schema sinh động từ DB.
- Ưu điểm của schema: gọn hơn, là tài liệu sống, check compatibility trước khi deploy, codegen cho ngôn ngữ static.
- Modes of dataflow:
- Qua database: "data outlives code" — dữ liệu 5 năm tuổi vẫn còn đó.
- Qua service (REST, RPC): RPC cố làm network call giống local call — sai lầm cơ bản (timeout, retry, idempotency, latency). Cần versioning API.
- Qua message broker (async): tách sender/receiver, buffer, retry, fan-out. Actor model (Akka, Erlang).
PART II — Distributed Data
Lý do phân tán: scalability, fault tolerance/high availability, latency (gần user). Kiến trúc chính: shared-nothing. Hai kỹ thuật cốt lõi: replication (sao chép) và partitioning/sharding (chia nhỏ) — thường dùng cùng nhau.
Chương 5 — Replication ⭐
Single-leader (leader–follower, master–slave)
- Write chỉ qua leader, leader gửi replication log tới follower.
- Sync vs async: sync an toàn nhưng 1 follower chậm làm treo cả hệ thống → thực tế dùng semi-synchronous (1 follower sync). Async thuần: leader chết có thể mất write đã confirm.
- Thêm follower mới: snapshot + catch up từ vị trí log (LSN / binlog coordinate).
- Failover khó: phát hiện leader chết (timeout bao lâu?), chọn leader mới, split brain (2 leader), write bị mất của leader cũ (sự cố GitHub + Redis/MySQL auto-increment).
- Cách triển khai log: statement-based (lỗi với
NOW(),RAND()), WAL shipping (gắn với storage engine), logical/row-based log (tách rời engine — nền tảng cho CDC), trigger-based.
Replication lag (eventual consistency) và cách xử lý
- Read-your-writes: đọc dữ liệu của chính user từ leader, hoặc theo timestamp write cuối.
- Monotonic reads: một user luôn đọc từ cùng một replica (hash user ID).
- Consistent prefix reads: write có quan hệ nhân quả phải được thấy đúng thứ tự (vấn đề đặc biệt với partitioned DB).
- Giải pháp gốc rễ: transaction — đừng giả vờ async replication là sync.
Multi-leader
- Use case: multi-datacenter, client offline (calendar app), collaborative editing.
- Vấn đề chính: write conflict. Cách xử lý: tránh conflict (route theo user), LWW (mất dữ liệu!), merge, lưu conflict rồi để app giải quyết (on write / on read), CRDT, operational transformation.
- Topology: circular, star, all-to-all (vấn đề thứ tự → dùng version vector).
Leaderless (Dynamo-style: Cassandra, Riak, Voldemort)
- Client gửi write/read tới nhiều replica song song.
- Quorum:
nreplica, write thành công khiwnode ack, read hỏirnode;w + r > nđảm bảo có ít nhất 1 node có giá trị mới nhất. Phổ biếnn=3, w=r=2. - Sửa dữ liệu cũ: read repair, anti-entropy (background).
- Giới hạn: quorum vẫn không đảm bảo linearizability (sloppy quorum, concurrent write, write thất bại một phần...).
- Sloppy quorum + hinted handoff: tăng availability khi network partition.
- Detecting concurrent writes: "happens-before" vs concurrent; dùng version number / version vector để phát hiện, trả siblings cho client merge.
Chương 6 — Partitioning (Sharding)
- Mục tiêu: dàn đều data + query; tránh skew và hot spot.
- Key range: giữ thứ tự, range scan tốt; hot spot khi key tuần tự (timestamp) → thêm prefix.
- Hash of key: đều hơn, mất range query. Cassandra: compound key — phần đầu hash để chia partition, phần sau sort trong partition.
- Hot key (celebrity): thêm random suffix vào key → phải đọc từ nhiều key rồi gộp. Việc này phải làm ở tầng application.
- Secondary index:
- By document (local index): ghi nhanh, đọc phải scatter/gather mọi partition (tail latency tệ).
- By term (global index): đọc nhanh, ghi chậm hơn và thường async.
- Rebalancing:
- ❌
hash mod N— đổi N thì gần như mọi key phải di chuyển. - ✅ Fixed number of partitions (nhiều hơn node, VD 1000 partition cho 10 node) — Riak, Elasticsearch, Couchbase.
- ✅ Dynamic partitioning (split/merge theo kích thước) — HBase, MongoDB.
- ✅ Proportional to nodes — Cassandra.
- Nên có người duyệt (manual/semi-auto) — auto rebalancing + auto failure detection có thể gây cascading failure.
- Request routing / service discovery: client → bất kỳ node / routing tier / client biết partition. Thường dùng ZooKeeper giữ metadata; hoặc gossip protocol.
Chương 7 — Transactions ⭐⭐ (rất hay hỏi)
ACID
- Atomicity: all-or-nothing, có thể abort và retry an toàn (nên gọi là abortability).
- Consistency: invariant của application — DB chỉ hỗ trợ một phần.
- Isolation: transaction đồng thời không thấy nhau (lý tưởng = serializable).
- Durability: đã commit thì không mất (WAL, replication) — không có durability tuyệt đối.
- BASE: Basically Available, Soft state, Eventual consistency — định nghĩa còn mơ hồ hơn ACID.
Isolation levels và anomaly chúng chặn
| Anomaly | Read Committed | Snapshot Isolation | Serializable |
|---|---|---|---|
| Dirty read | ✅ | ✅ | ✅ |
| Dirty write | ✅ | ✅ | ✅ |
| Read skew (non-repeatable read) | ❌ | ✅ | ✅ |
| Lost update | ❌ | ⚠️ (một số DB tự phát hiện) | ✅ |
| Write skew | ❌ | ❌ | ✅ |
| Phantom | ❌ | ⚠️ (read-only query thì OK) | ✅ |
- Read committed: row-level lock cho write; đọc giá trị đã commit gần nhất (giữ 2 phiên bản).
- Snapshot isolation / repeatable read: MVCC — mỗi transaction đọc một snapshot nhất quán. "Readers never block writers, writers never block readers". Quan trọng cho backup, analytic query. ⚠️ Tên "repeatable read" có nghĩa khác nhau giữa các DB.
- Lost update (read-modify-write): giải bằng atomic operation (
UPDATE ... SET x = x + 1), explicit lock (SELECT ... FOR UPDATE), tự phát hiện lost update, compare-and-set. Với replicated DB: dùng thao tác commutative hoặc CRDT. - Write skew: 2 transaction đọc cùng dữ liệu, cập nhật các object khác nhau dựa trên tiền đề đã bị thay đổi. VD: 2 bác sĩ cùng xin nghỉ trực → không còn ai trực. Giải: serializable, hoặc
SELECT FOR UPDATEcác row làm tiền đề, hoặc materializing conflicts. - Phantom: write ở một transaction làm thay đổi kết quả của một search query ở transaction khác.
3 cách đạt Serializable
- Actual serial execution (VoltDB, Redis, Datomic): 1 thread, transaction dạng stored procedure, data vừa RAM, transaction phải ngắn. Scale bằng partitioning (cross-partition thì chậm).
- Two-Phase Locking (2PL): shared lock cho read, exclusive lock cho write, giữ lock đến khi commit. Chặn phantom bằng predicate lock / index-range lock. Nhược: throughput kém, latency không ổn định, deadlock.
- Serializable Snapshot Isolation (SSI) (PostgreSQL ≥ 9.1, FoundationDB): optimistic — chạy trên snapshot, khi commit kiểm tra tiền đề đã đọc có bị thay đổi không; nếu có thì abort. Hiệu quả khi contention thấp.
Chương 8 — The Trouble with Distributed Systems ⭐
"Anything that can go wrong will go wrong." Phần mềm trên 1 máy là deterministic; hệ phân tán có partial failure — không deterministic.
- Unreliable networks (asynchronous packet network): request có thể mất, bị delay, remote node chết, response mất... Người gửi không phân biệt được các trường hợp → chỉ có timeout.
- Timeout ngắn → phát hiện nhanh nhưng dễ báo sai, gây cascading failure; dài → chờ lâu. Nên đo phân phối RTT và điều chỉnh động (Phi Accrual failure detector).
- Nguyên nhân delay: queueing (switch, OS, VM, TCP flow control). Network điện thoại (circuit) có latency bị chặn trên; Internet thì unbounded delay vì tối ưu cho bursty traffic.
- Unreliable clocks:
- Time-of-day clock (có thể nhảy lùi do NTP) vs monotonic clock (chỉ dùng đo khoảng thời gian).
- Clock drift, NTP sai lệch → LWW dựa trên timestamp có thể mất write. Timestamp nên đi kèm confidence interval (Google Spanner TrueTime).
- Process pause: GC stop-the-world, VM suspend, context switch, swap... có thể kéo dài vài giây → một node có thể tưởng mình vẫn còn giữ lease.
- Knowledge, truth and lies:
- The truth is defined by the majority — node không thể tự tin vào phán đoán của chính mình; cần quorum.
- Fencing token: lock service cấp số tăng dần; storage từ chối write có token cũ hơn → chống zombie leader.
- Byzantine faults (node nói dối): thường không cần xử lý trong data center (trừ blockchain, hàng không).
- System models: timing (synchronous / partially synchronous / asynchronous) × node failure (crash-stop / crash-recovery / Byzantine). Thực tế: partially synchronous + crash-recovery.
- Safety (điều xấu không bao giờ xảy ra) vs liveness (điều tốt cuối cùng sẽ xảy ra).
Chương 9 — Consistency & Consensus ⭐⭐
Linearizability (atomic / strong consistency)
- Hệ thống trông như chỉ có một bản sao dữ liệu, mọi thao tác atomic. Khi một read thấy giá trị mới thì mọi read sau đó cũng thấy giá trị mới (recency guarantee).
- ≠ Serializability: serializable là isolation của transaction (nhiều object); linearizable là recency trên một object. Cả hai = strict serializability (2PL, actual serial execution).
- Cần cho: leader election / distributed lock, uniqueness constraint (username), cross-channel timing dependency (upload ảnh + message queue).
- Cách triển khai: single-leader (có thể), consensus (có), multi-leader (không), leaderless quorum (thường không).
- CAP theorem: khi bị network partition, chọn linearizable hoặc available. Tên đúng hơn: "either Consistent or Available when Partitioned". CAP phạm vi hẹp, ít hữu ích thực tế. Lý do thật mà nhiều hệ thống bỏ linearizability là performance, không phải fault tolerance.
Ordering & causality
- Causal consistency: mạnh nhất trong các mô hình không bị chậm vì network delay, vẫn available khi partition.
- Linearizability ⇒ total order; causality ⇒ partial order.
- Lamport timestamp (counter, node ID): tạo total order nhất quán với causality, nhưng không đủ để giải uniqueness constraint ngay lúc đó (phải biết mọi thao tác khác).
- Total order broadcast (atomic broadcast): reliable delivery + totally ordered delivery. Tương đương consensus; là nền tảng của state machine replication, ZooKeeper, etcd.
Distributed transactions & consensus
- Two-Phase Commit (2PC): coordinator gửi prepare → mọi participant trả lời yes → commit. Hai "point of no return": participant đã vote yes, coordinator đã quyết định. Nếu coordinator chết sau prepare → participant in doubt, giữ lock chờ mãi (blocking).
- ≠ 2PL.
- XA transactions: làm giảm availability, coordinator là single point of failure, không tương thích với SSI.
- Fault-tolerant consensus — thuộc tính: uniform agreement, integrity, validity, termination. Thuật toán: Paxos, Raft, Zab, Viewstamped Replication.
- Dùng epoch number + quorum; mỗi epoch có leader duy nhất.
- Cần đa số node sống (3 node chịu 1 lỗi, 5 node chịu 2 lỗi).
- FLP: không có thuật toán consensus tất định luôn kết thúc trong mô hình async thuần — thực tế dùng timeout.
- ZooKeeper / etcd: dùng consensus để cung cấp linearizable atomic ops, total ordering (fencing token = zxid), failure detection (ephemeral node), change notification. Dùng cho: leader election, service discovery, partition assignment. Đừng tự viết consensus.
PART III — Derived Data
System of record (source of truth) vs derived data (cache, index, materialized view, ML model) — derived data có thể tái tạo được từ nguồn.
Chương 10 — Batch Processing
- 3 loại hệ thống: service (online, response time), batch (offline, throughput), stream (near-real-time).
- Unix philosophy: mỗi chương trình làm tốt một việc, interface chung (file / stdin–stdout), tách logic khỏi wiring.
cat | awk | sort | uniq -c | sort -rn | head—sorttự spill ra đĩa khi data lớn hơn RAM. - MapReduce + HDFS: map → shuffle (sort theo key) → reduce. Đưa computation tới data.
- Reduce-side join (sort-merge join): tổng quát nhưng tốn chi phí shuffle.
- Map-side join: broadcast hash join (bảng nhỏ vừa RAM), partitioned hash join, map-side merge join.
- Skew: hot key → Pig skewed join, Hive map-side join.
- Output của batch: build search index, key-value store để serve (ghi file DB mới rồi swap). Immutable input + ghi output mới → dễ rollback, human fault tolerance.
- Hadoop vs MPP database: Hadoop linh hoạt về dữ liệu (sushi principle: raw data is better), linh hoạt mô hình xử lý, chịu lỗi ở mức task.
- Beyond MapReduce: materialize state trung gian ra HDFS là lãng phí → dataflow engines (Spark, Tez, Flink) xử lý cả workflow như một DAG; Pregel / BSP cho graph; high-level API (Hive, Spark SQL) + declarative query optimizer.
Chương 11 — Stream Processing ⭐
- Event: bản ghi bất biến về một sự kiện đã xảy ra. Producer → broker → consumer.
- Messaging systems — hai câu hỏi quan trọng: producer nhanh hơn consumer thì sao (drop / buffer / backpressure)? Node chết thì có mất message không?
- AMQP/JMS-style broker (RabbitMQ, ActiveMQ): xoá message khi ack, load balancing giữa consumer, không đảm bảo thứ tự khi redelivery.
- Log-based broker (Kafka, Kinesis): append-only log chia partition, consumer giữ offset; thứ tự trong partition được đảm bảo; replay được; song song hoá theo số partition.
- Databases & streams:
- Dual writes (app ghi vào DB và cache/index) → race condition + partial failure → dữ liệu lệch nhau.
- Change Data Capture (CDC): đọc replication log của DB thành stream (Debezium, Maxwell) → derived system cập nhật theo đúng thứ tự. Log compaction giữ giá trị mới nhất cho mỗi key.
- Event sourcing: lưu mọi thay đổi dưới dạng event bất biến ở mức domain; state hiện tại = fold over events. Tách command (có thể bị từ chối) và event (fact đã xảy ra).
- Immutability: log là source of truth, database chỉ là cache của subset mới nhất của log. CQRS: tách hình dạng dữ liệu ghi và đọc.
- Processing streams: CEP, stream analytics (window, rate), maintaining materialized views, search on streams.
- Reasoning about time:
- Event time vs processing time — dùng processing time sẽ sai khi có delay/backlog.
- Straggler events: bỏ qua hoặc phát correction.
- Window types: tumbling, hopping, sliding, session.
- Stream joins: stream-stream (window join), stream-table (enrichment, dùng CDC giữ local copy), table-table (materialized view maintenance). Chú ý slowly changing dimension.
- Fault tolerance: microbatching (Spark Streaming), checkpointing (Flink), exactly-once = effectively-once thông qua atomic commit hoặc idempotence (lưu offset cùng với output).
Chương 12 — The Future of Data Systems
- Data integration: không có tool nào làm được hết → ghép nhiều tool, dùng log (total order) + derived data thay cho distributed transaction.
- Lambda architecture (batch + stream song song) vs thống nhất batch và stream (Kappa-style, Flink/Beam).
- Unbundling databases: coi cả tổ chức như một "database" lớn — index, cache, materialized view là derived views được duy trì bởi stream processor. "Database inside-out".
- Designing around dataflow: app code = hàm biến đổi derived state; tách state (DB) và logic (stateless service); đẩy thay đổi tới client (reactive / subscribe) thay vì polling.
- Aiming for correctness:
- End-to-end argument: tính đúng phải được đảm bảo end-to-end (VD: request ID idempotent xuyên suốt từ client tới DB), transaction của DB là không đủ.
- Enforcing constraints (uniqueness) qua log: partition theo giá trị cần unique, xử lý tuần tự.
- Timeliness vs integrity: timeliness (eventual) có thể vi phạm tạm thời; integrity (không mất/lỗi dữ liệu) thì không bao giờ được vi phạm. Nhiều nghiệp vụ chấp nhận vi phạm constraint tạm thời rồi bù trừ (apology/compensation).
- Trust, but verify: audit, kiểm tra tính toàn vẹn dữ liệu định kỳ, không tin mù quáng vào hardware/software.
- Doing the right thing: đạo đức dữ liệu — predictive analytics có thể tạo bias, vòng feedback; surveillance; privacy và consent. Dữ liệu là tài sản nhưng cũng là "toxic asset".
Ánh xạ sang System Design Interview
| Bài toán phỏng vấn | Kiến thức DDIA dùng đến |
|---|---|
| Twitter / News feed | Ch1 fan-out on write/read, hybrid cho celebrity; Ch6 partition by user ID |
| URL shortener | Ch6 hash partitioning; Ch9 unique ID (không cần consensus nếu dùng range cấp phát) |
| Chat app (WhatsApp) | Ch11 log-based broker, ordering per partition; Ch5 read-your-writes |
| Payment / ví điện tử | Ch7 isolation, lost update, write skew; Ch12 idempotency key, end-to-end |
| Rate limiter | Ch8 clock không đáng tin; Ch9 linearizable counter vs approximate |
| Distributed lock / leader election | Ch8 fencing token, lease; Ch9 ZooKeeper/etcd |
| Search / autocomplete | Ch3 full-text index; Ch11 CDC → Elasticsearch |
| Analytics dashboard | Ch3 column store, OLAP; Ch10 batch; Ch11 windowed aggregation |
| Key-value store (Dynamo) | Ch5 leaderless, quorum, hinted handoff, vector clock; Ch6 consistent hashing |
| Booking / bán vé (tránh double booking) | Ch7 write skew, SELECT FOR UPDATE, serializable; Ch9 uniqueness |
Câu hỏi tự kiểm tra
- Vì sao p99 quan trọng hơn trung bình? Tail latency amplification là gì?
- Khi nào chọn LSM-tree thay vì B-tree? Write amplification là gì?
- Vì sao row-store không phù hợp cho analytic query?
- Làm sao thêm một field vào message Protobuf mà không làm hỏng service cũ?
- Async replication có thể làm mất write đã được confirm như thế nào?
w + r > ncó đảm bảo linearizability không? Vì sao không?- Vì sao
hash mod Nlà cách rebalance tồi? - Cho ví dụ write skew. Snapshot isolation có chặn được không?
- So sánh 2PL và SSI: pessimistic vs optimistic, khi nào dùng cái nào?
- Vì sao không dùng time-of-day clock để sắp xếp event giữa các node?
- Fencing token giải quyết vấn đề gì mà lease không giải quyết được?
- Linearizability khác serializability thế nào? CAP nói gì thực sự?
- Vì sao 2PC bị gọi là blocking protocol?
- Dual writes nguy hiểm thế nào? CDC giải quyết ra sao?
- Event time vs processing time — khi nào gây ra sai số?
- "Exactly-once" trong stream processing thực chất được đạt như thế nào?