Chương 12 — The Future of Data Systems
🎯 Mục tiêu chương: Tổng hợp toàn bộ cuốn sách thành một tầm nhìn: xây ứng dụng bằng cách kết hợp nhiều hệ thống chuyên biệt qua dataflow (log, derived data, "unbundling databases"), đạt correctness mà không cần distributed transaction (end-to-end argument, idempotence, timeliness vs integrity, auditing), và nhìn lại trách nhiệm đạo đức của kỹ sư khi làm việc với dữ liệu về con người.
Chương này là quan điểm cá nhân của tác giả ("nên như thế nào" thay vì "đang như thế nào"). Mục tiêu vẫn là hệ thống reliable, scalable, maintainable — và thêm: robust, correct, evolvable, có ích cho con người.
Data Integration
Mỗi bài toán có nhiều lời giải với trade-off khác nhau (log-structured vs B-tree vs column store; single-leader vs multi-leader vs leaderless). Không phần mềm nào hợp với mọi hoàn cảnh — kể cả DB "general-purpose" cũng được thiết kế cho một kiểu workload. Ứng dụng phức tạp dùng dữ liệu theo nhiều cách → tất yếu phải ghép nhiều phần mềm lại.
Combining Specialized Tools by Deriving Data
- Ví dụ điển hình: OLTP DB + full-text search index (PostgreSQL có full-text nhưng đơn giản; search index thì không hợp làm system of record).
- Thêm nữa: data warehouse, batch/stream system, cache, denormalized object, ML/recommendation, notification… càng nhiều biểu diễn thì tích hợp càng khó.
- Tác giả phê phán câu "99% người chỉ cần X" — nói nhiều về kinh nghiệm người nói hơn là về công nghệ; nhu cầu tích hợp chỉ lộ ra khi nhìn dataflow toàn tổ chức.
Reasoning about dataflows
- Phải rõ: dữ liệu ghi vào đâu trước, biểu diễn nào derive từ nguồn nào.
- Ví dụ: ghi vào system of record DB → CDC → áp vào search index theo cùng thứ tự. Nếu CDC là cách duy nhất cập nhật index thì index chắc chắn nhất quán với DB (trừ bug).
- Cho ứng dụng ghi trực tiếp vào cả DB và index → quay lại vấn đề dual-write (Figure 11-4): không ai "nắm quyền" quyết định thứ tự.
- Nguyên tắc: dồn mọi input qua một hệ thống quyết định total order, rồi derive mọi biểu diễn khác theo đúng thứ tự đó (state machine replication). Dùng CDC hay event sourcing không quan trọng bằng nguyên tắc total order.
- Cập nhật derived data từ event log thường làm được deterministic và idempotent → dễ phục hồi.
Derived data versus distributed transactions
| Khía cạnh | Distributed transactions (2PC/XA) | Log-based derived data (CDC, event sourcing) |
|---|---|---|
| Quyết định thứ tự ghi | Lock (mutual exclusion, 2PL) | Thứ tự trong log |
| Đảm bảo hiệu lực đúng một lần | Atomic commit | Deterministic retry + idempotence |
| Timing guarantee | Thường linearizable, có read-your-own-writes | Asynchronous, mặc định không có |
| Fault tolerance | Một participant lỗi → abort, khuếch đại lỗi | Lỗi được cô lập, consumer lỗi bắt kịp sau |
| Tích hợp hệ thống khác loại | Khó, XA kém về hiệu năng và chịu lỗi | Dễ: log có thứ tự + consumer idempotent |
- Quan điểm tác giả: thiếu một giao thức distributed transaction tốt được hỗ trợ rộng rãi, log-based derived data là hướng hứa hẹn nhất để tích hợp hệ thống. Nhưng không nên bảo mọi người "eventual consistency là tất yếu, chịu đi" mà không hướng dẫn cách xử lý — phần "Aiming for Correctness" tìm điểm giữa.
The limits of total ordering
Total order log khả thi khi hệ thống nhỏ (single-leader DB làm đúng điều này), nhưng khi scale: - Throughput vượt 1 máy → phải partition → thứ tự giữa partition mơ hồ. - Nhiều datacenter địa lý → mỗi DC một leader → thứ tự giữa DC không xác định. - Microservices: mỗi service có state riêng → event từ các service khác nhau không có thứ tự. - Client offline-capable cập nhật state ngay → client và server thấy event theo thứ tự khác.
Total order broadcast ≡ consensus; hầu hết thuật toán consensus giả định 1 node xử lý hết throughput. Consensus scale vượt 1 node và chạy tốt geo-distributed vẫn là bài toán nghiên cứu mở.
Ordering events to capture causality
- Event không có quan hệ nhân quả thì thứ tự tùy ý cũng được; update cùng object thì route cùng partition là xong.
- Nhưng causality có thể tinh tế. Ví dụ unfriend: hai người chia tay, A unfriend B rồi gửi tin nhắn than phiền cho bạn bè còn lại, muốn B không thấy. Nếu friendship lưu một nơi, message lưu nơi khác, service notification có thể xử lý message-send trước unfriend → gửi thông báo nhầm cho B. Bản chất là một join giữa messages và friend list → vấn đề time-dependence của join.
- Chưa có lời giải đơn giản. Hướng tiếp cận:
- Logical timestamp (Lamport…) — total order không cần coordination, nhưng người nhận vẫn phải xử lý event đến không đúng thứ tự, cần thêm metadata.
- Log event ghi lại state user đã thấy trước khi ra quyết định, gán ID duy nhất; event sau tham chiếu ID đó để ghi nhận causal dependency (xem "Reads are events too").
- Conflict resolution (CRDT…) — tốt cho duy trì state, nhưng không giúp được khi có side effect bên ngoài (gửi notification).
Batch and Stream Processing
- Mục tiêu data integration: dữ liệu đúng dạng, ở đúng nơi. Batch và stream là công cụ để đạt điều đó; output là derived dataset (index, materialized view, recommendation, metric…).
- Khác biệt căn bản: stream xử lý unbounded, batch xử lý bounded; ranh giới đang mờ dần: Spark làm stream trên batch engine (microbatch), Flink làm batch trên stream engine. Giả lập lẫn nhau được nhưng hiệu năng khác (microbatch kém với hopping/sliding window).
Maintaining derived state
- Batch mang tính functional: hàm deterministic, pure, input immutable, output append-only. Stream mở rộng thêm managed, fault-tolerant state.
- Hàm deterministic với input/output rõ ràng giúp cả fault tolerance lẫn suy luận dataflow trong tổ chức.
- Có thể duy trì derived data đồng bộ (như secondary index trong cùng transaction), nhưng asynchrony là thứ làm hệ thống event log robust: lỗi được cô lập cục bộ, còn distributed transaction abort khi một participant lỗi → khuếch đại lỗi.
- Secondary index xuyên partition (term-partitioned phải ghi nhiều partition; document-partitioned phải đọc mọi partition) cũng scale và tin cậy hơn khi duy trì bất đồng bộ.
Reprocessing data for application evolution
- Stream: phản ánh thay đổi với độ trễ thấp. Batch: reprocess lượng lớn dữ liệu lịch sử để derive view mới.
- Không có reprocessing, schema evolution chỉ giới hạn ở thay đổi đơn giản (thêm field optional). Có reprocessing thì tái cấu trúc hoàn toàn dataset sang model mới.
- Ví dụ đường ray thế kỷ 19 ở Anh: nhiều khổ ray khác nhau; sau khi chuẩn hóa năm 1846, chuyển đổi bằng cách thêm ray thứ ba (dual gauge) để tàu cả hai khổ cùng chạy, chuyển dần, cuối cùng bỏ ray cũ. (Vẫn còn ngoại lệ như BART ở San Francisco dùng khổ khác.)
- Tương tự với derived view: duy trì schema cũ và mới song song như hai view derive từ cùng dữ liệu gốc, chuyển dần một phần nhỏ user sang view mới, tăng dần, rồi bỏ view cũ. Mỗi bước đều đảo ngược được → giảm rủi ro → dám đi nhanh hơn.
The lambda architecture
- Ý tưởng: dữ liệu vào được ghi dạng immutable event append vào dataset tăng dần (như event sourcing); chạy song song hai hệ thống: batch (Hadoop MapReduce) và stream (Storm).
- Stream layer tạo cập nhật nhanh, gần đúng; batch layer sau đó xử lý cùng tập event để tạo bản chính xác. Lý do: batch đơn giản, ít bug; stream bị coi là kém tin cậy; stream có thể dùng thuật toán xấp xỉ.
- Đóng góp tốt: phổ biến ý tưởng derive view từ immutable event và reprocess khi cần.
- Vấn đề thực tế:
- Phải duy trì cùng logic ở hai framework — kể cả có Summingbird thì vẫn phải vận hành, debug, tune hai hệ thống.
- Hai output phải merge khi phục vụ request — dễ với aggregation trên tumbling window, rất khó với join, sessionization, output không phải time series.
- Reprocess toàn bộ thường xuyên thì đắt → batch phải chạy incremental (mỗi giờ) → lại gặp straggler, window cắt ngang ranh giới batch → batch layer phức tạp như stream, trái mục tiêu giữ batch đơn giản.
Unifying batch and stream processing
Làm cả batch (reprocess lịch sử) và stream (event mới) trong cùng một hệ thống, cần: - Replay event lịch sử qua cùng engine xử lý stream (log-based broker replay được; một số stream processor đọc từ HDFS). - Exactly-once cho stream processor — output như không có lỗi, bỏ partial output của task lỗi. - Windowing theo event time, không theo processing time (processing time vô nghĩa khi reprocess). Ví dụ Apache Beam API, chạy trên Flink hoặc Google Cloud Dataflow.
| Tiêu chí | Lambda architecture | Unified batch/stream (Kappa-style, Beam/Flink) |
|---|---|---|
| Số hệ thống | Hai: batch layer + speed layer | Một engine |
| Logic nghiệp vụ | Viết/duy trì hai lần | Một lần |
| Output | Hai output, phải merge khi đọc | Một output |
| Reprocess | Chạy lại batch layer | Replay log qua cùng engine |
| Yêu cầu | Batch đơn giản, stream có thể xấp xỉ | Replay, exactly-once, event-time windowing |
| Nhược điểm | Phức tạp vận hành, merge khó, batch incremental phức tạp | Phụ thuộc engine stream đủ mạnh; reprocess lớn tốn thời gian |
Unbundling Databases
- Ở mức trừu tượng, DB, Hadoop và hệ điều hành đều lưu và xử lý/query dữ liệu — đều là hệ quản lý thông tin.
- Hai triết lý (cùng ra đời đầu 1970s):
- Unix: abstraction mỏng, mức thấp của phần cứng; pipe và file là chuỗi byte.
- Relational DB: abstraction mức cao che giấu cấu trúc disk, concurrency, crash recovery; SQL và transaction.
- Cái nào "đơn giản" hơn tùy góc nhìn. NoSQL có thể coi là mang tinh thần Unix (abstraction mức thấp) vào distributed OLTP. Tác giả cố dung hòa hai triết lý.
Composing Data Storage Technologies
Các tính năng DB: secondary index, materialized view, replication log, full-text index — đều có "bản song song" do batch/stream xây: search index từ batch, materialized view maintenance, CDC sang derived system.
Creating an index
CREATE INDEX: DB quét consistent snapshot của bảng, trích và sort giá trị, ghi index; rồi xử lý backlog write kể từ snapshot; sau đó giữ index cập nhật với mỗi transaction. Giống hệt setup follower mới và bootstrap CDC (initial snapshot). Tức là CREATE INDEX = reprocess dataset để derive view mới.
The meta-database of everything
- Nhìn như vậy, dataflow toàn tổ chức giống một database khổng lồ. Mỗi batch/stream/ETL chuyển dữ liệu từ dạng này sang dạng khác giống subsystem duy trì index/materialized view.
- Batch/stream processor ≈ trigger, stored procedure, routine duy trì materialized view; derived system ≈ các loại index (B-tree, hash, spatial…) — nhưng do nhiều phần mềm khác nhau, chạy trên máy khác nhau, do team khác nhau quản lý.
- Hai hướng để ghép chúng thành một hệ thống cohesive:
- Federated database (polystore) — unify reads: một giao diện query thống nhất trên nhiều storage engine (ví dụ PostgreSQL foreign data wrapper). Theo truyền thống relational: query language cao cấp, ngữ nghĩa đẹp, implementation phức tạp.
- Unbundled database — unify writes: làm cho việc nối các storage system một cách tin cậy (qua CDC, event log) trở nên dễ, như "tháo rời" chức năng duy trì index của DB ra để đồng bộ write xuyên công nghệ. Theo truyền thống Unix: công cụ nhỏ làm tốt một việc, giao tiếp qua API mức thấp thống nhất (pipe), ghép bằng ngôn ngữ cao hơn (shell).
| Tiêu chí | Federated database (polystore) | Unbundled database |
|---|---|---|
| Hợp nhất | Reads | Writes |
| Cơ chế | Query interface chung, map giữa các data model | Event log (CDC), consumer idempotent đồng bộ các storage |
| Truyền thống | Relational: một hệ tích hợp, ngôn ngữ cao cấp | Unix: tool nhỏ + pipe + shell |
| Độ khó | Tương đối quản lý được | Khó hơn — trọng tâm của chương |
| Ví dụ | PostgreSQL foreign data wrapper | MySQL → Debezium → Kafka → Elasticsearch/Redis |
Making unbundling work
- Đồng bộ write qua distributed transaction trên hệ thống khác loại là lời giải sai theo tác giả. Transaction bên trong một hệ thống thì ổn (ví dụ trong stream processor để exactly-once), nhưng khi dữ liệu vượt ranh giới công nghệ, event log bất đồng bộ + idempotent write robust và thực tế hơn nhiều — một abstraction đơn giản hơn, không cần giao thức transaction chuẩn hóa.
- Lợi thế lớn: loose coupling ở hai mức:
- Hệ thống: consumer chậm/lỗi thì log buffer, producer và consumer khác vẫn chạy; consumer sửa xong thì bắt kịp — lỗi được cô lập. Distributed transaction đồng bộ thì leo thang lỗi cục bộ thành sự cố lớn.
- Con người: các team phát triển, vận hành component độc lập; event log là interface đủ mạnh (durable, có thứ tự) và đủ tổng quát.
Unbundled versus integrated systems
- Unbundling không thay thế DB: vẫn cần DB để giữ state trong stream processor và phục vụ query output; MPP warehouse vẫn tốt cho analytic query.
- Chạy nhiều hạ tầng thì phức tạp (learning curve, config, quirks) → càng ít moving parts càng tốt. Một sản phẩm tích hợp có thể hiệu năng tốt và dễ dự đoán hơn cho workload nó được thiết kế. Xây cho scale không cần là premature optimization.
- Mục tiêu unbundling là breadth, không phải depth: kết hợp nhiều DB để phục vụ dải workload rộng hơn. Nếu một sản phẩm đáp ứng đủ nhu cầu thì cứ dùng nó.
What's missing?
- Chưa có "Unix shell" cho unbundled database — ngôn ngữ cao cấp, khai báo để ghép storage và processing.
- Mơ ước: khai báo
mysql | elasticsearchnhư pipe — tương đươngCREATE INDEXphiên bản unbundled: index toàn bộ dữ liệu MySQL vào Elasticsearch rồi tự động theo dõi và áp mọi thay đổi, không cần code tùy biến. - Tương tự: khai báo materialized view/cache cho query phức tạp (kể cả recursive query trên graph). Nghiên cứu sớm: differential dataflow.
Designing Applications Around Dataflow
- Còn được gọi là "database inside-out" (tên bài talk của tác giả 2014) — tác giả coi đây là design pattern, không phải "kiến trúc mới". Ý tưởng tổng hợp từ dataflow language (Oz, Juttle), functional reactive programming (Elm), logic programming (Bloom). Thuật ngữ "unbundling" do Jay Kreps đề xuất.
- Spreadsheet analogy: đặt công thức vào một ô (tổng một cột), input đổi thì kết quả tự tính lại. Đó chính là điều ta muốn ở tầng hệ thống dữ liệu: record đổi → index, cache, aggregation phụ thuộc tự cập nhật, không cần lo chi tiết. VisiCalc đã có từ 1979! Khác biệt: hệ thống dữ liệu còn phải fault-tolerant, scalable, durable, và tích hợp công nghệ khác nhau của nhiều team theo thời gian.
Application code as a derivation function
Mỗi derived dataset qua một hàm biến đổi:
- Secondary index: chọn giá trị cột, sort — đơn giản, có sẵn qua CREATE INDEX.
- Full-text index: language detection, word segmentation, stemming/lemmatization, sửa chính tả, synonym → inverted index; cơ bản có sẵn, nâng cao cần tinh chỉnh theo domain.
- ML model: derive từ training data qua feature extraction, phân tích thống kê; output model derive từ input + model. Feature engineering rất đặc thù ứng dụng.
- Cache: aggregation theo dạng hiển thị trên UI; đổi UI có thể phải đổi cách populate và rebuild cache.
Khi hàm derive không phải loại "chuẩn" thì cần code tùy biến — chỗ DB yếu: trigger, stored procedure, UDF chỉ là tính năng phụ.
Separation of application code and state
- DB về lý thuyết có thể là môi trường chạy code, nhưng không hợp với nhu cầu hiện đại: dependency/package management, version control, rolling upgrade, monitoring, metrics, gọi network service. Mesos, YARN, Docker, Kubernetes làm tốt việc chạy code hơn.
- Hợp lý: một phần chuyên lưu trữ bền vững, phần khác chuyên chạy code, tương tác nhưng độc lập.
- Web app ngày nay: stateless service + state ở DB. Đùa của dân functional: "We believe in the separation of Church and state" (Alonzo Church, lambda calculus không có mutable state).
- DB như biến mutable dùng chung truy cập đồng bộ qua mạng — nhưng như hầu hết ngôn ngữ lập trình, không subscribe được thay đổi, chỉ poll (observer pattern không có sẵn). Subscribe change mới đang nổi lên.
Dataflow: Interplay between state changes and application code
- Tư duy dataflow: code ứng dụng phản ứng với thay đổi state ở một nơi bằng cách gây thay đổi state ở nơi khác (giống trigger, cập nhật secondary index, actor, tuple spaces thập niên 1980).
- Unbundling = áp dụng ý tưởng này để tạo derived dataset ngoài DB chính (cache, search, ML, analytics) bằng stream processing và messaging.
- Duy trì derived data ≠ chạy job bất đồng bộ (mục đích truyền thống của messaging):
- Thứ tự thay đổi quan trọng (nhiều view derive từ cùng log phải xử lý cùng thứ tự) → broker redeliver gây đảo thứ tự không hợp; dual write bị loại.
- Fault tolerance tối quan trọng: mất một message là derived dataset lệch vĩnh viễn. Nhiều actor system mặc định giữ state và message trong RAM → mất khi crash.
- Thứ tự ổn định + xử lý chịu lỗi là yêu cầu khắt khe nhưng rẻ và robust hơn distributed transaction; stream processor hiện đại làm được ở quy mô lớn và cho chạy code tùy ý như operator — ghép như Unix pipe.
Stream processors and services
- Microservices: chia chức năng thành service giao tiếp đồng bộ (REST) → lợi ích chính là scalability về tổ chức (loose coupling giữa team).
- Dataflow có đặc điểm tương tự nhưng giao tiếp một chiều, bất đồng bộ qua message stream.
- Ví dụ tỷ giá (exchange rate): mua hàng niêm yết một loại tiền, trả bằng loại khác:
- Microservices: code xử lý mua hàng gọi exchange-rate service/DB lấy tỷ giá hiện tại (network request đồng bộ).
- Dataflow: code mua hàng subscribe trước stream cập nhật tỷ giá, lưu vào DB local; khi xử lý mua chỉ query local.
- Cách 2 nhanh hơn và robust khi service khác lỗi — "request mạng nhanh nhất, tin cậy nhất là không có request nào". Thay RPC bằng stream-table join giữa purchase event và exchange-rate update. (Cache tỷ giá trong microservice cũng phải poll hoặc subscribe — tức là quay về dataflow.)
- Join này time-dependent: reprocess purchase sau này thì tỷ giá đã khác → cần tỷ giá lịch sử tại thời điểm mua. Dù query hay subscribe đều phải xử lý.
Observing Derived State
- Write path: khi thông tin được ghi, đi qua các stage batch/stream, mọi derived dataset được cập nhật — làm eager, bất kể có ai hỏi.
- Read path: khi phục vụ request, đọc từ derived dataset, xử lý thêm, trả kết quả — làm lazy, chỉ khi có người hỏi.
- Derived dataset là nơi write path gặp read path (Figure 12-1) — trade-off giữa công sức lúc ghi và lúc đọc. (Eager vs lazy evaluation trong functional programming.)
Materialized views and caching
- Full-text search: write cập nhật index cho mọi term; read tìm từng từ, áp Boolean AND/OR.
- Không index: write ít việc, read phải quét mọi document (như
grep) — đắt. - Precompute kết quả mọi query có thể: read cực nhẹ nhưng write vô hạn (tập query vô hạn) — không khả thi.
- Cache kết quả query phổ biến: một materialized view, cần cập nhật khi document mới khớp.
- ⇒ Vai trò của cache, index, materialized view: dịch chuyển ranh giới giữa read path và write path — làm nhiều hơn khi ghi để đỡ việc khi đọc.
- Quay về ví dụ Twitter ở Chương 1: fan-out timeline, và ranh giới khác nhau cho celebrity vs user thường — "sau 500 trang ta đã quay lại điểm xuất phát".
Stateful, offline-capable clients
- Mô hình client/server với client stateless quá phổ biến tới mức ta quên còn lựa chọn khác.
- Single-page app và mobile app có nhiều state local, local storage → offline-first: làm việc với DB local, sync nền khi có mạng. Mạng di động chậm/chập chờn nên đây là lợi thế lớn.
- Coi state trên thiết bị là cache của state trên server: pixel trên màn hình là materialized view của model object trong app; model object là replica local của state ở datacenter.
Pushing state changes to clients
- Trang web thường: browser đọc dữ liệu một lần, không biết khi server thay đổi → stale cache trừ khi reload/poll (RSS thực chất cũng là poll).
- Server-sent events (EventSource), WebSockets: giữ kết nối TCP mở, server chủ động push thay đổi.
- Theo mô hình write/read path: push thay đổi tới client = kéo dài write path tới tận người dùng cuối. Khởi tạo vẫn cần read path lấy state ban đầu, sau đó dựa vào stream thay đổi.
- Thiết bị offline → giống consumer offset của log-based broker: reconnect và đọc tiếp, không mất message. Mỗi thiết bị là một subscriber nhỏ của một stream nhỏ.
End-to-end event streams
- Elm, React/Flux/Redux đã quản lý state client bằng stream event (user input, server response) — giống event sourcing.
- Mở rộng tự nhiên: server push event thay đổi state vào pipeline phía client → thay đổi chảy end-to-end: từ tương tác trên thiết bị này, qua event log, derived system, stream processor, tới UI người xem trên thiết bị khác, độ trễ có thể < 1 giây. Instant messaging, online game đã làm vậy.
- Vì sao không làm cho mọi ứng dụng? Giả định stateless client + request/response ăn sâu trong DB, thư viện, framework, protocol; rất ít datastore hỗ trợ subscribe (một request → một stream response theo thời gian). Cần chuyển từ request/response sang publish/subscribe dataflow. Lời khuyên: khi thiết kế hệ thống dữ liệu, hãy tính đến việc subscribe thay đổi, không chỉ query state hiện tại.
Reads are events too
- Thường: write đi qua event log, read là network request tạm thời tới node lưu dữ liệu. Nhưng có thể biểu diễn read request cũng là stream event, đưa cả read và write qua stream processor; processor trả kết quả read ra output stream.
- Khi đó phục vụ request = thực hiện stream-table join giữa stream read query và DB; read event phải route tới đúng partition (như co-partition khi join). Read một lần: đi qua join rồi quên; subscribe: join bền vững với event quá khứ và tương lai.
- Một số framework cho phép query state bên trong stream processor từ bên ngoài → stream processor thành một DB đơn giản.
- Lợi ích của log read event: theo dõi causal dependency và data provenance — tái hiện user đã thấy gì trước khi quyết định. Ví dụ shop online: ngày giao dự kiến và tình trạng kho hiển thị ảnh hưởng quyết định mua → cần ghi lại kết quả query đó. Đổi lại tốn storage và I/O (bài toán nghiên cứu mở); nếu đã log request cho vận hành thì biến log thành nguồn request cũng không khác nhiều.
Multi-partition data processing
- Query một partition thì đưa qua stream là thừa, nhưng mở ra khả năng thực thi phân tán query phức tạp nhiều partition dùng hạ tầng routing, partitioning, join sẵn có của stream processor.
- Storm distributed RPC: đếm số người đã thấy một URL trên Twitter = hợp các tập follower của mọi người đã tweet URL đó (dữ liệu partition theo user).
- Fraud prevention: đánh giá rủi ro một giao dịch dựa trên reputation score của IP, email, địa chỉ thanh toán, địa chỉ giao hàng — mỗi DB reputation partition khác nhau → chuỗi join với dataset partition khác nhau.
- Query graph của MPP DB tương tự. Nếu cần multi-partition join thì dùng DB có sẵn thường đơn giản hơn; nhưng coi query là stream là lựa chọn cho ứng dụng lớn vượt giới hạn giải pháp off-the-shelf.
Aiming for Correctness
- Stateless read-only service lỗi thì sửa và restart là xong; stateful system nhớ "mãi mãi" nên lỗi cũng tồn tại mãi → cần suy nghĩ kỹ.
- ACID là công cụ chính trong ~40 năm, nhưng nền móng yếu hơn tưởng: weak isolation level gây bối rối; một số nơi bỏ hẳn transaction để lấy hiệu năng với ngữ nghĩa lộn xộn; "consistency" được nói nhiều nhưng định nghĩa mơ hồ. Jepsen (Kyle Kingsbury) chỉ ra khoảng cách giữa cam kết và hành vi thật khi có lỗi mạng/crash. Kể cả DB hoàn hảo, ứng dụng vẫn phải dùng đúng.
- Serializability và atomic commit có giá: thường chỉ trong một datacenter, giới hạn scale và fault tolerance. Transaction không biến mất nhưng không phải lời cuối cùng.
The End-to-End Argument for Databases
Dùng DB có đảm bảo mạnh (serializable) không có nghĩa ứng dụng hết mất/hỏng dữ liệu: bug ứng dụng ghi sai hay xóa dữ liệu thì transaction không cứu được. Immutability giúp, nhưng không phải thuốc tiên.
Exactly-once execution of an operation
- Xử lý lỗi: bỏ (mất dữ liệu) hoặc thử lại (có thể lần đầu đã thành công → xử lý 2 lần). Xử lý 2 lần cũng là data corruption: tính tiền khách 2 lần, đếm tăng 2 lần.
- Exactly-once = hiệu ứng cuối như không có lỗi. Cách hiệu quả: idempotence — nhưng biến thao tác không idempotent thành idempotent cần metadata (tập operation ID đã áp) và fencing khi failover.
Duplicate suppression
- TCP dùng sequence number để loại gói trùng — nhưng chỉ trong một kết nối TCP.
- Ví dụ chuyển tiền (Example 12-1):
UPDATE+11 cho tài khoản 1234, −11 cho 4321,COMMIT. Client mất kết nối sau khi gửiCOMMITnhưng trước khi nhận phản hồi → không biết commit hay chưa. Retry trên kết nối mới → nằm ngoài phạm vi TCP → có thể chuyển $22 thay vì $11. Ví dụ kinh điển về atomicity nhưng thực ra không đúng, ngân hàng thật không làm vậy. - 2PC tách transaction khỏi kết nối TCP (coordinator reconnect được) — vẫn chưa đủ.
- Còn mạng giữa thiết bị người dùng và app server: user gửi HTTP POST trên mạng di động yếu, mất phản hồi → thấy lỗi → retry ("Are you sure you want to submit this form again?"). Với web server là request mới, với DB là transaction mới. Post/Redirect/Get không giúp khi POST timeout.
Operation identifiers
- Phải xét luồng end-to-end của request: sinh ID duy nhất cho thao tác (UUID trong hidden form field, hoặc hash các field của form) ở client; submit 2 lần thì cùng ID; truyền ID tới tận DB.
- Example 12-2: bảng
requestscó UNIQUE(request_id); trong transactionINSERTrequest rồi mới update số dư. ID đã tồn tại →INSERTlỗi → transaction abort → không áp 2 lần. Uniqueness constraint được DB đảm bảo đúng kể cả ở weak isolation (khác với check-then-insert ở tầng ứng dụng dễ dính write skew). - Bảng
requestscòn đóng vai trò event log (hướng event sourcing): update số dư có thể derive ở consumer downstream, miễn xử lý exactly-once — lại dùng request ID.
The end-to-end argument
- Saltzer, Reed, Clark (1984): một chức năng chỉ có thể được implement đầy đủ và đúng với sự hiểu biết và trợ giúp của ứng dụng ở hai đầu của hệ thống giao tiếp; cung cấp nó như tính năng của chính hệ thống giao tiếp là không thể (bản không đầy đủ ở tầng thấp đôi khi hữu ích như tối ưu hiệu năng).
- Duplicate suppression: TCP (mức kết nối), stream processor exactly-once (mức message) đều không ngăn được user submit lại khi timeout → cần transaction ID truyền từ client tới DB.
- Integrity check: checksum của Ethernet, TCP, TLS phát hiện hỏng gói trên mạng nhưng không phát hiện bug phần mềm hai đầu hay hỏng trên disk → cần end-to-end checksum.
- Encryption: mật khẩu WiFi chỉ chống nghe lén WiFi; TLS chống tấn công mạng nhưng không chống server bị xâm nhập → chỉ end-to-end encryption chống được tất cả.
- Tính năng tầng thấp vẫn hữu ích (giảm xác suất lỗi ở tầng cao — không có TCP sắp xếp gói thì HTTP hỏng liên tục), chỉ là không đủ cho correctness end-to-end.
Applying end-to-end thinking in data systems
- Ứng dụng phải tự có biện pháp end-to-end (như duplicate suppression) — đáng tiếc vì fault tolerance khó làm đúng.
- Transaction là abstraction tốt: gom nhiều vấn đề (concurrent write, vi phạm constraint, crash, mất mạng, hỏng disk) thành 2 kết quả commit/abort — nhưng chưa đủ, và đắt khi xuyên công nghệ. Không dùng distributed transaction thì phải tự làm fault tolerance trong code ứng dụng — và tác giả nghi hầu hết làm sai → mất/hỏng dữ liệu.
- Cần tìm abstraction mới cho correctness end-to-end theo đặc thù ứng dụng mà vẫn hiệu năng và vận hành tốt ở quy mô lớn.
Enforcing Constraints
Tập trung vào uniqueness constraint: username/email duy nhất, không trùng tên file, không hai người đặt cùng ghế. Các ràng buộc tương tự: số dư không âm, không bán quá tồn kho, phòng họp không đặt chồng.
Uniqueness constraints require consensus
- Nhiều request đồng thời cùng giá trị → phải quyết định cái nào thắng → consensus.
- Cách phổ biến: một leader quyết định mọi thứ; cần chịu lỗi leader thì lại quay về consensus.
- Scale bằng partition theo giá trị cần unique (request ID, hash username).
- Asynchronous multi-master bị loại (hai master có thể cùng chấp nhận giá trị trùng). Muốn từ chối ngay write vi phạm thì coordination đồng bộ là không tránh được.
Uniqueness in log-based messaging
- Log đảm bảo mọi consumer thấy message cùng thứ tự = total order broadcast ≡ consensus. Stream processor đọc một partition tuần tự, single-thread → partition log theo giá trị cần unique thì processor quyết định deterministic ai đến trước.
- Ví dụ claim username:
- Mỗi request username thành message, append vào partition theo hash(username).
- Stream processor đọc tuần tự, dùng DB local ghi username đã lấy; còn trống → đánh dấu đã lấy, phát success ra output stream; đã có → phát rejection.
- Client theo dõi output stream, chờ kết quả cho request của mình.
- Scale bằng tăng số partition. Áp dụng cho mọi loại constraint: nguyên tắc là mọi write có thể xung đột được route vào cùng partition và xử lý tuần tự; processor dùng logic tùy ý để validate (giống Bayou thập niên 1990).
Multi-partition request processing
- Example 12-2 có thể dính 3 partition: request ID, tài khoản nhận, tài khoản trả. Cách truyền thống: atomic commit xuyên 3 partition → ép vào total order với mọi transaction khác trên các partition đó → throughput giảm.
- Cách dùng partitioned log, không atomic commit:
- Request chuyển tiền A → B được client gán request ID duy nhất, append vào partition theo request ID.
- Stream processor đọc log request, với mỗi request phát 2 message: debit tới A (partition theo A) và credit tới B (partition theo B), kèm request ID gốc.
- Processor khác consume stream debit/credit, deduplicate theo request ID, áp vào số dư.
- Vì sao cần bước 1–2: nếu client gửi trực tiếp debit và credit thì cần atomic commit giữa 2 partition. Thay vào đó ghi durable request thành một message duy nhất (single-object write luôn atomic), rồi derive debit/credit từ nó.
- Processor bước 2 crash → resume từ checkpoint, có thể phát trùng instruction, nhưng deterministic nên giống hệt → bước 3 dedupe bằng request ID.
- Muốn chống thấu chi: thêm processor partition theo tài khoản trả, duy trì số dư và validate; chỉ transaction hợp lệ mới vào log bước 1.
- Kết quả: cùng correctness (mỗi request áp đúng một lần vào cả hai tài khoản) mà không cần atomic commit — chia transaction multi-partition thành các stage partition khác nhau + end-to-end request ID.
Timeliness and Integrity
- Transaction thường linearizable: commit xong là mọi reader thấy ngay. Với stream nhiều stage, consumer bất đồng bộ; client có thể chờ message trên output stream (như uniqueness check), nhưng tính đúng của check không phụ thuộc việc chờ — chờ chỉ để báo kết quả.
- Tác giả tách "consistency" thành hai yêu cầu:
| Tiêu chí | Timeliness | Integrity |
|---|---|---|
| Ý nghĩa | User thấy hệ thống ở trạng thái cập nhật | Không hỏng dữ liệu: không mất, không mâu thuẫn, không sai |
| Khi bị vi phạm | Tạm thời — chờ rồi thử lại là hết | Vĩnh viễn — phải kiểm tra và sửa tường minh |
| Khẩu hiệu | "Eventual consistency" | "Perpetual inconsistency" |
| Ví dụ đảm bảo | Linearizability (CAP), read-after-write | Atomicity, durability, derived view đúng (index đủ record) |
| Ví dụ vi phạm (thẻ tín dụng) | Giao dịch 24h qua chưa hiện trên sao kê — bình thường | Số dư ≠ tổng giao dịch + số dư kỳ trước; bị trừ tiền mà merchant không nhận — thảm họa |
| Độ quan trọng | Thường chấp nhận nới lỏng | Hầu như luôn bắt buộc |
Correctness of dataflow systems
- ACID cho cả timeliness (linearizability) lẫn integrity (atomic commit) nên người ta ít phân biệt. Event-based dataflow tách chúng ra.
- Exactly-once / effectively-once là cơ chế giữ integrity: mất event hoặc áp 2 lần đều phá integrity → cần delivery chịu lỗi + duplicate suppression.
- Cách đạt integrity không cần atomic commit:
- Biểu diễn nội dung write là một message duy nhất (ghi atomic dễ) — hợp event sourcing.
- Derive mọi cập nhật state khác từ message đó bằng hàm deterministic (giống stored procedure).
- Truyền request ID do client sinh qua mọi tầng → dedupe end-to-end, idempotence.
- Message immutable, cho phép reprocess derived data → dễ phục hồi khi có bug.
Loosely interpreted constraints
- Uniqueness truyền thống đòi consensus (qua một node/partition) — stream cũng không tránh được. Nhưng nhiều ứng dụng thật chấp nhận ràng buộc lỏng hơn:
- Hai người cùng đăng ký username/ghế → xin lỗi một người, mời chọn lại = compensating transaction.
- Đặt hàng vượt tồn kho → nhập thêm, xin lỗi vì trễ, giảm giá. Giống hệt khi xe nâng cán hỏng hàng trong kho — quy trình xin lỗi vốn đã phải có.
- Hãng bay, khách sạn overbook cố ý; dù không overbook vẫn cần bồi thường khi hủy chuyến do thời tiết, đình công.
- Rút quá số dư → phí thấu chi; giới hạn rút mỗi ngày để giới hạn rủi ro.
- Chi phí xin lỗi thường thấp (email sửa lỗi, hoàn tiền trùng); là quyết định kinh doanh. Nếu chấp nhận được thì có thể ghi lạc quan, kiểm tra constraint sau — vẫn validate trước những việc khó đảo ngược (như tiền đã ra khỏi ATM).
- Các ứng dụng này cần integrity (không mất đặt chỗ, không mất tiền) nhưng không cần timeliness trong việc cưỡng chế constraint.
Coordination-avoiding data systems
- Hai nhận xét:
- Dataflow giữ được integrity cho derived data không cần atomic commit, linearizability, hay coordination đồng bộ xuyên partition.
- Nhiều ứng dụng ổn với constraint lỏng, vi phạm tạm thời rồi sửa, miễn integrity được giữ.
- ⇒ Coordination-avoiding data system: hiệu năng và fault tolerance tốt hơn. Ví dụ chạy multi-leader nhiều datacenter, replicate bất đồng bộ giữa region, mỗi DC hoạt động độc lập — timeliness yếu (không linearizable) nhưng integrity mạnh.
- Serializable transaction vẫn hữu ích ở phạm vi nhỏ; không cần XA. Coordination đồng bộ chỉ đặt ở chỗ thật cần (trước thao tác không thể phục hồi).
- Góc nhìn "số lời xin lỗi": coordination giảm xin lỗi do inconsistency nhưng tăng xin lỗi do outage/chậm. Không thể về 0 — tìm điểm cân bằng.
Trust, but Verify
- Mọi lập luận dựa trên system model: giả định process crash, mạng trễ/mất; nhưng giả định dữ liệu sau
fsynckhông mất, RAM không hỏng, CPU nhân đúng. Thực tế là xác suất, không nhị phân. - Dữ liệu có thể hỏng khi nằm yên trên disk, hỏng trên mạng mà lọt TCP checksum. Tác giả từng thấy crash report chỉ giải thích được bằng bit-flip ngẫu nhiên trong RAM; rowhammer lật bit bằng pattern truy cập bộ nhớ, dùng để phá bảo mật. Hiếm, nhưng đủ nhiều thiết bị thì sẽ xảy ra.
Maintaining integrity in the face of software bugs
- Bug phần mềm không bị checksum tầng thấp bắt được. Ngay DB lâu đời cũng có bug: MySQL từng không giữ đúng uniqueness constraint, PostgreSQL serializable từng có write skew.
- Code ứng dụng ít được review/test hơn nhiều → nhiều bug hơn; nhiều app không dùng đúng foreign key/unique constraint.
- "C" trong ACID giả định transaction không có bug; dùng weak isolation sai là mất integrity.
Don't just blindly trust what they promise
- Hỏng dữ liệu là sớm muộn → phải có cách phát hiện = auditing. Tài chính coi trọng audit vì ai cũng biết sai sót xảy ra.
- HDFS, Amazon S3 không tin hoàn toàn disk: tiến trình nền liên tục đọc lại file, so với replica, di chuyển file — giảm nguy cơ silent corruption.
- Muốn chắc dữ liệu còn thì phải đọc lại và kiểm tra; thỉnh thoảng thử restore backup.
A culture of verification
- Cần nhiều hệ thống self-validating / self-auditing liên tục kiểm tra integrity thay vì tin mù quáng.
- Văn hóa ACID khiến ta tin vào công nghệ và bỏ qua auditability; rồi NoSQL mang consistency yếu hơn, storage kém trưởng thành phổ biến — mà cơ chế audit chưa phát triển → càng nguy hiểm.
Designing for auditability
- Transaction sửa nhiều object thì khó biết vì sao; CDC log insert/update/delete cũng không nói lên ý định; logic quyết định là tạm thời, không tái tạo được.
- Event-based system audit tốt hơn: input là một event immutable, state derive deterministic, lặp lại được → chạy lại cùng log với cùng code ra cùng state.
- Dataflow tường minh → provenance rõ: hash để kiểm tra event storage không hỏng; chạy lại batch/stream processor (hoặc derive dư thừa song song) để kiểm tra derived state. Hỗ trợ time-travel debugging.
The end-to-end argument again
- Không tin được từng component → kiểm tra integrity định kỳ, theo kiểu end-to-end: kiểm tra càng bao trùm nhiều hệ thống càng ít chỗ cho corruption lọt; kiểm tra cả pipeline thì mọi disk, network, service, thuật toán trên đường đi đều được kiểm gián tiếp.
- Giống automated test: kiểm tra liên tục → tự tin → đi nhanh hơn, dám đổi công nghệ.
Tools for auditable data systems
- Ít hệ thống coi auditability là ưu tiên; audit table tự làm khó đảm bảo integrity; ký log bằng HSM chống sửa nhưng không đảm bảo đúng transaction được đưa vào.
- Blockchain / distributed ledger (Bitcoin, Ethereum, Ripple, Stellar…): về bản chất là distributed DB mà replica do các tổ chức không tin nhau giữ, liên tục kiểm tra integrity lẫn nhau, dùng consensus. Tác giả hoài nghi về Byzantine fault tolerance, thấy proof of work cực kỳ lãng phí, throughput thấp — nhưng ý tưởng integrity checking đáng chú ý.
- Merkle tree (cây hash) chứng minh hiệu quả một record có trong dataset; certificate transparency dùng Merkle tree kiểm tra chứng chỉ TLS. Có thể các thuật toán này sẽ phổ biến trong data system nói chung nếu giảm được overhead.
Doing the Right Thing
- Mọi hệ thống có mục đích, mọi hành động có hệ quả ngoài ý muốn. Kỹ sư có trách nhiệm cân nhắc hệ quả và chọn thế giới muốn sống.
- Nhiều dataset là về con người — hành vi, sở thích, danh tính → đối xử với sự nhân văn và tôn trọng. Có hướng dẫn (ACM Code of Ethics) nhưng ít được áp dụng.
- Công nghệ tự nó không tốt/xấu — cách dùng mới quan trọng (như search engine hay súng). Trách nhiệm đạo đức thuộc về cả kỹ sư.
Predictive Analytics
- Dự đoán thời tiết, dịch bệnh là một chuyện; dự đoán tái phạm, vỡ nợ, claim bảo hiểm thì ảnh hưởng trực tiếp đời người.
- Tổ chức thiên về "nếu nghi ngờ thì nói không" (mất cơ hội rẻ, rủi ro đắt). Nhưng người bị gắn nhãn rủi ro (đúng hay sai) có thể nhận hàng loạt lời từ chối: việc làm, bay, bảo hiểm, thuê nhà, tài chính → "algorithmic prison". Tư pháp giả định vô tội; hệ thống tự động có thể loại người khỏi xã hội mà không cần chứng cứ, khó kháng cáo.
Bias and discrimination
- Thuật toán không nhất thiết tốt hay tệ hơn người; hy vọng dữ liệu công bằng hơn cảm tính. Nhưng hệ thống tự suy ra luật từ dữ liệu, pattern mờ đục; bias trong input sẽ bị học và khuếch đại.
- Luật cấm phân biệt theo đặc điểm được bảo vệ (sắc tộc, tuổi, giới…), nhưng các feature khác có thể tương quan (mã bưu chính, IP ở khu phân biệt chủng tộc → dự đoán chủng tộc). Châm biếm: "machine learning is like money laundering for bias".
- Predictive analytics chỉ ngoại suy quá khứ; quá khứ phân biệt đối xử thì mã hóa nó. Muốn tương lai tốt hơn cần moral imagination — chỉ con người có. Dữ liệu và model là công cụ, không phải chủ nhân.
Responsibility and accountability
- Người sai thì chịu trách nhiệm và bị kháng cáo; thuật toán sai thì ai chịu? Xe tự lái gây tai nạn? Credit scoring phân biệt? Giải thích được cho tòa án không?
- Credit score truyền thống dựa trên lịch sử vay thực của chính bạn, sửa lỗi được. Predictive analytics dựa trên "ai giống bạn và họ đã hành xử thế nào" → rập khuôn (stereotyping) theo nơi ở; bị xếp nhầm nhóm thì gần như không thể kháng cáo.
- Dữ liệu thống kê: phân phối tổng thể đúng nhưng từng cá nhân có thể sai (tuổi thọ trung bình 80 không có nghĩa bạn mất đúng sinh nhật 80). Tin mù quáng vào dữ liệu là nguy hiểm.
- Analytics có thể dùng để hỗ trợ người cần nhất — hoặc để doanh nghiệp săn mồi nhắm vào người dễ tổn thương (vay lãi cao, bằng cấp vô giá trị).
Feedback loops
- Recommendation system chỉ cho xem điều người ta đã đồng ý → echo chamber, định kiến, tin sai, phân cực (đã thấy trong bầu cử).
- Vòng phản hồi tự củng cố: nhà tuyển dụng dùng credit score; bạn gặp khó khăn tài chính ngoài ý muốn → trễ hóa đơn → điểm giảm → khó tìm việc → nghèo hơn → điểm càng giảm. Vòng xoáy đi xuống núp sau vẻ "toán học chặt chẽ".
- Dùng systems thinking: xét cả hệ thống (máy lẫn người), hỏi hệ thống khuếch đại bất bình đẳng hay chống lại nó; cảnh giác hệ quả ngoài ý muốn.
Privacy and Tracking
- Người dùng tự nhập dữ liệu để hệ thống xử lý theo ý họ → hệ thống phục vụ user (user là khách hàng). Nhưng khi hành vi bị tracking như tác dụng phụ, dịch vụ có lợi ích riêng có thể xung đột với user.
- Tracking có ích: xếp hạng search theo click, "người thích X cũng thích Y", A/B test. Nhưng nếu mô hình kinh doanh là quảng cáo thì advertiser mới là khách hàng; tracking chi tiết hơn, lưu lâu hơn, dựng hồ sơ từng người → quan hệ đáng gọi bằng từ nặng hơn: surveillance.
Surveillance
- Thí nghiệm tư duy: thay "data" bằng "surveillance": "surveillance-driven organization", "real-time surveillance streams", "surveillance warehouse", "surveillance scientists"… — nghe khác hẳn. (Tác giả đùa: Designing Surveillance-Intensive Applications.)
- Ta đã xây hạ tầng giám sát lớn nhất lịch sử: IoT, microphone kết nối internet ở mọi phòng (điện thoại, smart TV, trợ lý giọng nói, baby monitor, đồ chơi) với bảo mật tồi. Chế độ toàn trị cũng chỉ dám mơ; khác biệt là do doanh nghiệp thu thập thay vì chính phủ.
- "Tôi không có gì để giấu" — chỉ đúng với người thuận theo cấu trúc quyền lực, không thuộc nhóm thiểu số bị đàn áp. Mục đích "vô hại" (quảng cáo cá nhân hóa) mờ đi khi kết hợp với predictive analytics: phí bảo hiểm xe gắn với thiết bị theo dõi, bảo hiểm sức khỏe phụ thuộc fitness tracker; cảm biến smartwatch có thể đoán được bạn gõ gì (kể cả mật khẩu).
Consent and freedom of choice
- "User đã đồng ý điều khoản" — có vấn đề:
- User không hiểu dữ liệu nào được thu và xử lý thế nào; privacy policy che nhiều hơn làm rõ → không có meaningful consent. Dữ liệu một user còn nói về người khác không đồng ý gì. Derived dataset kết hợp toàn bộ user base và nguồn ngoài càng không thể hiểu.
- Quan hệ một chiều, bất cân xứng: không thương lượng được; điều khoản do dịch vụ đặt.
- Không dùng dịch vụ cũng không phải lựa chọn tự do: dịch vụ thiết yếu cho tham gia xã hội (smartphone, Facebook, Google) là de facto bắt buộc; network effect tạo chi phí xã hội khi rời bỏ. Chỉ người có đặc quyền (thời gian, kiến thức, không sợ mất cơ hội) mới từ chối được.
Privacy and use of data
- "Privacy is dead" vì người ta đăng mọi thứ lên mạng — sai. Privacy không phải giữ bí mật mọi thứ, mà là quyền quyết định tiết lộ gì cho ai — một quyền tự chủ.
- Surveillance chuyển quyền privacy từ cá nhân sang người thu thập ("hãy tin chúng tôi"). Công ty giữ bí mật kết quả vì tiết lộ sẽ bị thấy "creepy"; thông tin riêng tư lộ gián tiếp qua công cụ target quảng cáo (ví dụ người mắc một bệnh). Dù không định danh lại được, người dùng vẫn mất quyền quyết định.
- Công ty thường chỉ lo không bị coi là creepy thay vì hỏi thu thập xâm phạm tới đâu; dữ liệu đúng sự thật vẫn có thể gây tổn thương (gợi ký ức đau). Cần cơ chế xử lý khi dữ liệu sai/không phù hợp — thuật toán không tự hiểu điều này.
- Privacy setting chỉ kiểm soát user khác thấy gì; bản thân dịch vụ vẫn truy cập không giới hạn. Việc chuyển giao quyền privacy quy mô lớn như vậy là chưa từng có: trước đây giám sát tốn kém, thủ công; quan hệ tin cậy (bác sĩ, luật sư) bị ràng buộc bởi đạo đức, pháp luật.
Data as assets and power
- Dữ liệu hành vi bị gọi là "data exhaust" (khí thải, vô giá trị) — nhưng nếu quảng cáo nuôi dịch vụ thì dữ liệu hành vi là tài sản cốt lõi, còn ứng dụng chỉ là mồi để user nạp thêm dữ liệu. Data broker mua bán dữ liệu cá nhân bí mật; startup được định giá theo "eyeballs".
- Ai cũng muốn dữ liệu: công ty, chính phủ (thỏa thuận ngầm, ép buộc, pháp lý, đánh cắp); công ty phá sản thì dữ liệu bị bán; rò rỉ thường xuyên → dữ liệu là "toxic asset", "hazardous material".
- Thu thập dữ liệu phải cân nhắc mọi chính phủ tương lai: "lắp đặt công nghệ có thể một ngày giúp hình thành police state là vệ sinh công dân kém". Tri thức là quyền lực; soi xét người khác mà tránh bị soi xét là một dạng quyền lực quan trọng.
Remembering the Industrial Revolution
- Cách mạng công nghiệp mang lại tăng trưởng nhưng kèm ô nhiễm, điều kiện lao động tồi, lao động trẻ em; phải rất lâu mới có quy định bảo vệ môi trường, an toàn lao động, cấm lao động trẻ em — chi phí kinh doanh tăng nhưng xã hội hưởng lợi lớn.
- Bruce Schneier: dữ liệu là vấn đề ô nhiễm của thời đại thông tin, bảo vệ privacy là thách thức môi trường. Con cháu sẽ phán xét cách ta xử lý việc thu thập và lạm dụng dữ liệu.
Legislation and self-regulation
- European Data Protection Directive (1995): dữ liệu cá nhân phải thu cho mục đích cụ thể, rõ ràng, hợp pháp, và tương xứng, không quá mức với mục đích (tiền thân của GDPR — quy định mới đang được soạn khi sách viết).
- Nhưng mâu thuẫn trực tiếp với triết lý Big Data: tối đa hóa thu thập, kết hợp dataset, khám phá cho mục đích chưa biết trước.
- Công ty phản đối quy định vì cản trở đổi mới — phần nào có lý (dữ liệu y tế có thể cứu người); cân bằng khó.
- Cần thay đổi văn hóa: coi user là con người đáng được tôn trọng, không phải metric cần tối ưu; tự điều chỉnh thu thập và xử lý dữ liệu; giáo dục user về cách dữ liệu được dùng. Quyền kiểm soát dữ liệu cá nhân như công viên quốc gia — không bảo vệ sẽ thành bi kịch của mảnh đất công.
- Hành động cụ thể: không giữ dữ liệu mãi mãi, purge khi không cần (mâu thuẫn với immutability nhưng giải quyết được); cưỡng chế access control bằng giao thức mật mã thay vì chỉ bằng policy.
⚠️ Hiểu lầm & cạm bẫy thường gặp
- "Dùng serializable transaction là dữ liệu an toàn" — không chống được bug ứng dụng, retry trùng từ client (HTTP POST lặp), hay corruption phần cứng/phần mềm. Cần biện pháp end-to-end.
- "TCP/Kafka exactly-once đã lo chống trùng" — chỉ trong phạm vi một kết nối/một framework. Muốn chống trùng thật phải có request ID sinh ở client truyền tới tận DB.
- Retry transaction không idempotent (chuyển tiền) sau timeout → có thể áp 2 lần.
- Đánh đồng "consistency" thành một khái niệm — tách timeliness (tạm thời, chờ là hết) và integrity (vĩnh viễn, phải sửa). Eventual consistency ≠ chấp nhận mất dữ liệu.
- "Microservices + distributed transaction/2PC để giữ nhất quán" — XA kém chịu lỗi và khuếch đại sự cố; ưu tiên log + consumer idempotent (saga/compensation).
- Unbundling mọi thứ khi không cần — nhiều moving parts = nhiều vận hành; nếu một DB đủ dùng thì dùng nó. Unbundling là cho breadth.
- Lambda architecture là best practice — duy trì 2 codebase, merge output khó; hệ thống hiện đại unify batch/stream được.
- Nghĩ mọi constraint phải được kiểm tra đồng bộ trước khi ghi — nhiều nghiệp vụ chấp nhận compensating transaction (xin lỗi, hoàn tiền); chỉ coordination ở chỗ không thể đảo ngược.
- Nghĩ total order luôn làm được — partition, multi-DC, microservices, offline client đều phá vỡ nó; causal dependency (unfriend + message) dễ bị mất.
- Tin tưởng mù quáng vào disk, DB, backup — phải audit, đọc lại, thử restore.
- "Dữ liệu khách quan nên thuật toán công bằng" — bias trong dữ liệu bị học và khuếch đại; proxy feature (zip code) lộ thuộc tính được bảo vệ.
- "User đã đồng ý ToS nên thu thập gì cũng được" — không có meaningful consent khi user không hiểu và không có lựa chọn thực sự.
💼 Áp dụng thực tế & phỏng vấn
- Idempotency key: Stripe, PayPal, API thanh toán yêu cầu header
Idempotency-Key— chính là operation ID end-to-end của Example 12-2. Trong phỏng vấn "Design payment system", luôn nhắc idempotency key + unique constraint + ledger append-only + reconciliation (auditing). - Transactional outbox + CDC để cập nhật search/cache/service khác thay vì dual write hay 2PC; saga với compensating transaction cho quy trình nhiều service (đặt vé, đơn hàng).
- Chuyển tiền giữa tài khoản ở partition khác nhau: trả lời bằng pattern 3 bước — log request với request ID, derive debit/credit, consumer dedupe — thay vì distributed transaction.
- Unique username/ticket booking ở quy mô lớn: partition theo key cần unique, xử lý tuần tự single-thread mỗi partition (Kafka partition theo hash(username) hoặc seat ID); client chờ kết quả trên output stream.
- Overbooking/inventory: thảo luận trade-off giữa kiểm tra đồng bộ (lock, reservation có TTL) và optimistic + compensation — thể hiện hiểu biết về timeliness vs integrity.
- Multi-region: coordination-avoiding design — multi-leader async cho phần lớn dữ liệu, chỉ đồng bộ ở chỗ cần (thanh toán, uniqueness toàn cục).
- Schema migration / tái cấu trúc dữ liệu: dựng view mới song song từ log, chuyển traffic dần (giống dual gauge, shadow read, dark launch), rollback dễ.
- Dataflow thay RPC: stream-table join (tỷ giá, giá sản phẩm, config) với bản sao local → giảm latency và phụ thuộc runtime giữa service; nhớ vấn đề time-dependent join khi reprocess.
- Real-time UI: WebSocket/SSE push thay đổi (chat, collaborative editing, live dashboard, Firebase-like sync), offline-first với sync bằng offset/cursor.
- Batch + stream: nêu Kappa/unified (Flink, Beam, Kafka replay) thay vì Lambda khi được hỏi thiết kế analytics pipeline.
- Data integrity ops: checksum end-to-end, reconciliation job định kỳ so sánh nguồn và derived store, test restore backup, audit log bất biến (Merkle tree/hash chain).
- Privacy by design: data minimization, retention policy, xóa theo GDPR (crypto-shredding: mã hóa dữ liệu mỗi user bằng key riêng, xóa key để "xóa" khỏi log immutable), access control bằng mật mã — hay được hỏi ở công ty lớn.