23-09-2026 - Designing Data-Intensive Applications

Mục tiêu buổi đọc

  • Nắm mục tiêu của sách qua Preface.
  • Đọc Chapter 1 để tạo khung nhìn về reliability, scalability, maintainability.
  • Ghi lại câu hỏi hoặc khái niệm cần tách thành concept note sau khi đọc.

Kế hoạch đọc

  • Preface: mục tiêu, audience, scope, outline.
  • Chapter 1: Thinking About Data Systems.
  • Reliability: hardware faults, software errors, human errors.
  • Scalability: load, performance, coping with load.
  • Maintainability: operability, simplicity, evolvability.

Trọng tâm hiện tại — Thinking About Data Systems (trang 4–5)

  • Giải thích vì sao database, cache và queue được gom dưới khái niệm Data System.
  • Đọc Figure 1-1 và lần theo luồng giữa API, application code, database, cache, search index và message queue.
  • Xác định guarantee nào thuộc trách nhiệm của data system designer khi ghép nhiều công cụ.
  • Trả lời bốn câu hỏi thiết kế: correctness, degraded performance, scalability và API.

Reliability (trang 6–10)

  • Phân biệt fault và failure; giải thích mục tiêu của fault tolerance.
  • Hardware Faults: giải thích vì sao 10.000 disk có thể dẫn tới khoảng một disk hỏng mỗi ngày dù MTTF của từng disk rất dài.
  • Hardware Faults: so sánh component redundancy với software fault tolerance ở cấp machine/service.
  • Software Errors: giải thích vì sao lỗi systematic và correlated có thể vượt qua replication.
  • Software Errors: nhận diện bad input, runaway process, dependency failure và cascading failure.
  • Human Errors: thiết kế guardrail, sandbox, gradual rollout, rollback và telemetry.
  • How Important Is Reliability?: đánh giá hậu quả của downtime, kết quả sai và mất dữ liệu đối với user.
  • Nêu rõ trường hợp nào có thể chấp nhận giảm reliability và guarantee nào bị hy sinh.

Scalability (trang 10–18)

  • Describing Load: chọn load parameter phản ánh bottleneck thay vì chỉ dùng average.
  • Describing Load: vẽ lại ba chiến lược Twitter gồm fan-out on read, fan-out on write và hybrid.
  • Describing Load: giải thích 4.6k tweet/s × 75 follower ≈ 345k timeline write/s và ảnh hưởng của celebrity skew.
  • Describing Performance: trả lời hai câu hỏi resource cố định và performance cố định.
  • Describing Performance: phân biệt throughput, response time, latency, p50, p95 và p99.
  • Describing Performance: giải thích head-of-line blocking, tail latency amplification và bẫy load generator tuần tự.
  • Approaches for Coping with Load: so sánh scale up, scale out, elastic và manual scaling.
  • Approaches for Coping with Load: giải thích vì sao stateful system khó phân tán hơn stateless service.
  • Nêu load assumption nào có thể buộc hệ phải đổi kiến trúc khi tăng thêm một order of magnitude.

Maintainability (trang 18–22)

  • Operability: Making Life Easy for Operations — liệt kê nhiệm vụ của operations và cách data system giảm routine work.
  • Operability: giải thích vì sao monitoring, predictable model, safe default và manual control phải đi cùng nhau.
  • Simplicity: Managing Complexity — nhận diện state explosion, tight coupling, tangled dependency, hack và special case.
  • Simplicity: phân biệt essential complexity với accidental complexity.
  • Simplicity: giải thích abstraction giúp giảm detail phải reasoning như thế nào và khi nào abstraction che mất failure mode.
  • Evolvability: Making Change Easy — phân biệt refactoring cục bộ với thay đổi ở cấp data system.
  • Evolvability: dùng ví dụ Twitter để mô tả migration giữa fan-out on read, fan-out on write và hybrid.
  • Vẽ quan hệ Operability + Simplicity -> Evolvability -> Maintainability và ghi rõ giới hạn của cách rút gọn này.

Summary (trang 22–23)

  • Phân biệt functional requirements với nonfunctional requirements.
  • Tóm tắt Reliability, Scalability và Maintainability bằng một câu cho mỗi yếu tố.
  • Giải thích vì sao ba yếu tố phải được đánh giá cùng nhau thay vì tối ưu độc lập.
  • Dùng checklist cuối chương để đánh giá một hệ thống mình từng xây dựng.
  • Xem Summary.

Ghi chú trong lúc đọc

Câu hỏi cần trả lời sau khi đọc

  • Data-intensive khác compute-intensive ở đâu?
  • Reliability, scalability, maintainability có thể xung đột nhau như thế nào?
  • Mình sẽ dùng chỉ số nào để mô tả load/performance cho một hệ backend cụ thể?

Gợi ý trả lời toàn bộ câu hỏi

Các đáp án dưới đây tổng hợp từ 01 - Reliable, Scalable, and Maintainable Applications và các concept liên quan. Checklist vẫn để trống vì có đáp án không đồng nghĩa với việc người học đã tự nhớ và giải thích được.

Thinking About Data Systems

Vì sao database, cache và queue được gom dưới khái niệm Data System?

Ranh giới giữa các category ngày càng mờ: một datastore như Redis có thể được dùng như queue, còn Kafka là message queue nhưng có durability gần với database. Quan trọng hơn, một ứng dụng thường phải ghép database, cache, search index và queue để đáp ứng yêu cầu mà một tool riêng lẻ không xử lý được. Khi application code phối hợp các tool và cung cấp một API thống nhất, toàn bộ tổ hợp trở thành một Data System chuyên biệt.

Luồng của Figure 1-1 hoạt động như thế nào?

Client
-> API
-> application code
   -> kiểm tra in-memory cache cho read
   -> đọc/ghi primary database khi cache miss hoặc có write
   -> gửi search request tới full-text index
   -> đẩy asynchronous task vào message queue
 
Primary database thay đổi
-> application code cập nhật/invalidate cache
-> application code cập nhật search index
 
Message queue
-> worker application code
-> side effect ra bên ngoài, ví dụ gửi email

Guarantee nào thuộc trách nhiệm của data system designer?

  • Dữ liệu vẫn đúng và đầy đủ khi component bên trong gặp fault.
  • Cache được invalidate hoặc update phù hợp sau write.
  • Search index được đồng bộ với primary database theo guarantee đã công bố.
  • Client nhận performance đủ ổn định khi một phần system degraded.
  • API che implementation detail nhưng diễn đạt đúng service contract.

Các guarantee này không tự xuất hiện chỉ vì từng tool hoạt động tốt; chúng phụ thuộc vào coordination logic giữa các tool.

Bốn câu hỏi thiết kế cốt lõi

  1. Correctness: invariant nào phải luôn đúng, và fault ở component nào có thể phá invariant đó?
  2. Degraded performance: khi cache, index, queue hoặc database chậm, service tiếp tục, degrade hay từ chối request như thế nào?
  3. Scalability: load parameter nào tăng và cần thêm resource hoặc đổi data flow ra sao?
  4. API: client cần guarantee nào, và chi tiết triển khai nào nên được giấu sau interface?

Reliability

Fault, failure và mục tiêu của fault tolerance

  • Fault: một component lệch khỏi đặc tả.
  • Failure: toàn system không còn cung cấp service yêu cầu cho user.
  • Fault tolerance: detect, contain và recover để fault cục bộ không lan thành failure ở boundary user quan sát.

Không thể loại bỏ mọi fault, nên cần nêu rõ fault model: loại fault nào system có thể chịu và loại nào nằm ngoài guarantee.

Vì sao 10.000 disk có thể có khoảng một disk hỏng mỗi ngày?

MTTF 10–50 năm là kỳ vọng cho từng disk, không phải cho cả cluster. Khi có 10.000 disk hoạt động song song, xác suất tổng hợp tăng mạnh: sự kiện hiếm trên một disk trở thành sự kiện thường xuyên ở cấp fleet. Vì vậy cluster lớn phải coi disk failure là hoạt động vận hành bình thường, không phải ngoại lệ bất ngờ.

Component redundancy khác software fault tolerance thế nào?

Cách tiếp cậnBoundary bảo vệVí dụGiới hạn
Component redundancyThành phần trong một machine/datacenterRAID, dual power supply, backup generatorKhông đủ khi mất cả machine hoặc cloud instance
Software fault toleranceService chạy trên nhiều machineFailover, chuyển traffic, rolling upgradeCoordination và software complexity cao hơn

Component redundancy làm từng machine bền hơn; software fault tolerance chấp nhận machine có thể mất nhưng service vẫn tiếp tục.

Vì sao lỗi systematic và correlated vượt qua replication?

Replica thường chạy cùng code và dựa trên cùng assumption. Nếu một bad input kích hoạt cùng bug trên mọi replica, tất cả có thể fail đồng thời. Replication chỉ chống tốt failure độc lập; nó không tự bảo vệ khỏi lỗi logic giống nhau hoặc shared dependency bị hỏng.

Bốn software failure mode cần nhận diện

  • Bad input kích hoạt bug trên nhiều instance.
  • Runaway process dùng hết CPU, memory, disk hoặc bandwidth dùng chung.
  • Dependency chậm, không phản hồi hoặc trả corrupted response.
  • Cascading failure khiến fault nhỏ lan qua chuỗi dependency.

Biện pháp gồm làm rõ assumption, test corner case, process isolation, crash/restart, monitoring và runtime self-check cho invariant.

Thiết kế chống human error

  • Guardrail trong API/admin UI để thao tác đúng dễ hơn thao tác nguy hiểm.
  • Sandbox gần production để thử nghiệm mà không ảnh hưởng real user.
  • Test nhiều cấp trước deployment.
  • Gradual rollout để giới hạn blast radius.
  • Rollback nhanh và công cụ recompute dữ liệu.
  • Telemetry để phát hiện assumption hoặc constraint bị vi phạm.

Guardrail không nên quá cứng; nếu cản công việc hợp lệ, operator sẽ tìm workaround và làm mất lớp bảo vệ.

Reliability quan trọng đến đâu?

Không chỉ safety-critical system mới cần reliability. Business application sai có thể gây mất năng suất và legal risk; ecommerce outage làm mất doanh thu và uy tín; mất ảnh hoặc dữ liệu cá nhân có thể không thể bù đắp. Mức reliability cần dựa trên hậu quả của downtime, kết quả sai, khả năng phục hồi dữ liệu và kỳ vọng user.

Khi nào có thể chấp nhận giảm reliability?

Có thể cân nhắc ở prototype cho thị trường chưa được kiểm chứng hoặc service có operational margin rất thấp. Tuy nhiên cần ghi rõ:

  • guarantee nào bị giảm;
  • user sẽ thấy failure nào;
  • dữ liệu có phục hồi được không;
  • chi phí failure có thực sự thấp hơn chi phí phòng ngừa và recovery không.

Scalability

Chọn load parameter như thế nào?

Chọn đại lượng giải thích bottleneck của kiến trúc: requests/second, read/write ratio, concurrent users, cache hit rate, payload size, data volume hoặc fan-out distribution. Không chỉ dùng average; nếu workload có skew, tail của distribution có thể quyết định capacity.

Ba chiến lược Twitter home timeline

Fan-out on read
Post -> ghi tweet một lần
Read -> lấy followee -> đọc tweet -> merge/sort
 
Fan-out on write
Post -> tìm follower -> ghi vào timeline cache từng follower
Read -> đọc timeline đã materialize
 
Hybrid
User thường -> fan-out on write
Celebrity -> lấy tweet khi read rồi merge vào timeline

Fan-out on read có write rẻ nhưng read đắt. Fan-out on write chuyển computation sang write để phục vụ lượng timeline read rất lớn. Hybrid xử lý riêng celebrity để tránh một tweet tạo hàng chục triệu write.

Vì sao 4.6k tweet/s thành khoảng 345k timeline write/s?

fan-out write rate ≈ tweet rate × follower trung bình
                   ≈ 4.6k × 75
                   ≈ 345k write/s

Đây chỉ là average. User có hơn 30 triệu follower tạo spike cực lớn; capacity planning phải xét distribution follower và tần suất đăng, không chỉ phép nhân trung bình.

Hai câu hỏi khi mô tả performance

  1. Nếu tăng load nhưng giữ nguyên CPU, memory và network, performance suy giảm thế nào?
  2. Nếu tăng load nhưng muốn performance không đổi, cần tăng resource bao nhiêu hoặc đổi architecture thế nào?

Throughput, response time, latency và percentile

  • Throughput: lượng record/job xử lý trong một đơn vị thời gian, thường quan trọng với batch.
  • Response time: tổng thời gian client chờ, gồm queueing, service time và network delay.
  • Latency theo nghĩa chặt của nguồn: thời gian request chờ được xử lý.
  • p50: 50% request nhanh hơn threshold này.
  • p95: 95% request nhanh hơn threshold này.
  • p99: 99% request nhanh hơn threshold này; 1% request nằm ở tail chậm hơn.

Mean dễ che outlier, còn percentile cho biết trải nghiệm của các nhóm request khác nhau.

Head-of-line blocking, tail amplification và load-test bias

  • Head-of-line blocking: một số request chậm giữ worker/resource, khiến request phía sau phải chờ dù bản thân xử lý nhanh.
  • Tail latency amplification: một end-user request gọi nhiều backend chỉ hoàn tất khi call chậm nhất xong; càng nhiều call, xác suất gặp ít nhất một tail request càng cao.
  • Load-test bias: nếu generator đợi response rồi mới gửi request tiếp theo, queue bị giữ ngắn giả tạo và kết quả tốt hơn production. Generator phải gửi request độc lập với response time.

So sánh cách ứng phó với load

CáchĐiểm mạnhTrade-off
Scale upĐơn giản, ít coordinationMachine lớn đắt và có giới hạn
Scale outChia tải qua nhiều machineCoordination, state và operations phức tạp
ElasticPhản ứng với load khó đoánCó thể tạo operational surprise
ManualDễ dự đoán và kiểm soátCần capacity planning và phản ứng con người

Vì sao stateful system khó phân tán hơn stateless service?

Stateless request có thể route tới nhiều node gần như thay thế cho nhau. Stateful system phải quyết định state nằm ở đâu, được partition/replicate thế nào, request tới đúng node ra sao, và làm gì khi node mất hoặc data được rebalance. Coordination và correctness guarantee làm scale out phức tạp hơn.

Load assumption nào có thể buộc đổi kiến trúc?

  • Read/write ratio thay đổi mạnh.
  • Fan-out distribution xuất hiện celebrity hoặc hot key.
  • Data volume vượt khả năng một node.
  • Payload nhỏ, nhiều request chuyển thành payload lớn, ít request hoặc ngược lại.
  • p99 requirement chặt hơn.
  • Load từ ổn định chuyển sang burst khó đoán.

Khi load tăng thêm một order of magnitude, bottleneck có thể chuyển tầng; chỉ thêm machine có thể không còn đủ.

Maintainability

Operations làm gì và data system giảm routine work ra sao?

Operations theo dõi health, khôi phục service, tìm root cause, patch platform, quản lý dependency, capacity planning, deployment, configuration, migration, security và organizational knowledge.

Data system giảm routine work bằng monitoring rõ, automation support, standard integration, không phụ thuộc một machine, documentation, operational model dễ dự đoán, safe defaults, manual override và self-healing có kiểm soát.

Vì sao monitoring, predictable model, safe default và manual control phải đi cùng nhau?

  • Monitoring cho biết system đang ở trạng thái nào.
  • Predictable model giúp đoán tác động của hành động.
  • Safe default giảm xác suất thao tác thường ngày gây lỗi.
  • Manual control cho phép can thiệp khi automation hoặc default không phù hợp.

Thiếu một trong bốn, operator có thể thấy sự cố nhưng không hiểu, hiểu nhưng không can thiệp được, hoặc automation tự hành động theo cách khó dự đoán.

Dấu hiệu accidental complexity

  • State space bùng nổ.
  • Module tight coupling.
  • Dependency đan xen.
  • Naming và terminology không nhất quán.
  • Performance hack che thiết kế ban đầu.
  • Special case ở module này để bù vấn đề của module khác.

Essential complexity khác accidental complexity

Essential complexity thuộc bản chất bài toán user cần giải quyết. Accidental complexity xuất hiện do implementation và cách tổ chức system. Simplicity không xóa bài toán khó; nó loại phần phức tạp không mang giá trị cho user.

Abstraction giảm complexity như thế nào và có thể sai ở đâu?

Abstraction gom implementation detail sau interface/contract rõ, giảm lượng detail mỗi engineer phải giữ trong đầu. High-level language che machine code; SQL che storage structure, concurrency và nhiều chi tiết recovery.

Abstraction trở nên nguy hiểm khi che mất failure mode, latency hoặc guarantee mà caller cần biết, hoặc khi detail leak ra khiến mọi caller phải hiểu implementation bên trong.

Refactoring cục bộ khác thay đổi cấp data system

Refactoring cục bộ thường nằm trong một application hoặc vài file và giữ external behavior. Thay đổi cấp data system có thể đi qua nhiều service, schema, storage, data flow, API và guarantee; thường cần migration dần trong khi system vẫn phục vụ user.

Twitter minh họa evolvability như thế nào?

Twitter chuyển từ fan-out on read sang fan-out on write, rồi hybrid. Đây là thay đổi nơi computation xảy ra, cách timeline được materialize, cache strategy và load distribution. Nó cho thấy evolvability không chỉ là sửa code mà là thay đổi architecture khi assumption về workload không còn đúng.

Quan hệ Operability, Simplicity, Evolvability và giới hạn của mô hình

Operability -> quan sát và kiểm soát system
Simplicity  -> hiểu boundary, dependency và assumption
            ↓
Evolvability -> thay đổi/migration với risk chấp nhận được
            ↓
Maintainability -> vận hành và phát triển system lâu dài

Đây không phải phương trình đầy đủ. Evolvability còn phụ thuộc test, compatibility, data migration và organization; operability tốt cũng không tự loại complexity; abstraction đơn giản nhưng sai guarantee có thể làm reliability kém hơn.

Summary

Functional và nonfunctional requirements

  • Functional requirements mô tả system phải làm gì: lưu, đọc, tìm kiếm hoặc xử lý dữ liệu.
  • Nonfunctional requirements mô tả phẩm chất system cần có: security, reliability, scalability, compatibility, compliance và maintainability.

Mỗi trụ cột trong một câu

  • Reliability: system tiếp tục cung cấp đúng service khi hardware, software hoặc con người tạo fault.
  • Scalability: system có strategy giữ performance khi một load parameter cụ thể tăng.
  • Maintainability: engineering và operations có thể vận hành, hiểu và thay đổi system hiệu quả trong thời gian dài.

Vì sao không thể tối ưu độc lập?

Scale out tăng capacity nhưng làm coordination và operations phức tạp hơn. Automation tăng operability và scalability nhưng có thể tạo correlated failure. Abstraction làm system dễ hiểu nhưng nếu che mất fault hoặc latency thì reliability giảm. Thiết kế tốt cần cân bằng cả ba tại service boundary.

Ví dụ đánh giá một backend tổng quát

Đây là mẫu để áp dụng, không phải mô tả hệ thống cá nhân của người học.

Giả sử backend gồm API, database, cache và message queue:

  1. Functional: nhận request, lưu dữ liệu, trả query, chạy task bất đồng bộ.
  2. Reliability: xác định hành vi khi database/cache/queue chậm hoặc mất; kiểm tra retry có tạo duplicate không; có rollback và monitoring không.
  3. Scalability: đo requests/s theo endpoint, read/write ratio, payload, concurrency, cache hit rate và queue depth; theo dõi p50/p95/p99.
  4. Maintainability: kiểm tra dashboard/runbook, predictable deployment, coupling giữa module, khả năng migration schema và thay queue/cache mà không phá API guarantee.

Ba câu hỏi cuối ngày

Data-intensive khác compute-intensive ở đâu?

Compute-intensive application bị giới hạn chủ yếu bởi raw CPU computation. Data-intensive application thường bị giới hạn bởi data volume, data complexity và tốc độ dữ liệu thay đổi; bài toán chính là lưu trữ, truy xuất, tìm kiếm, truyền và phối hợp dữ liệu qua nhiều component.

Reliability, scalability và maintainability xung đột thế nào?

  • Scale out tăng capacity và fault tolerance nhưng làm coordination, debugging và operations khó hơn.
  • Thêm redundancy tăng reliability nhưng tăng chi phí, state và failure mode.
  • Abstraction giảm complexity nhưng có thể che failure semantics cần thiết.
  • Tối ưu p99 có thể tăng chi phí và complexity vận hành.
  • Automation giúp scale và operations nhưng bug automation có thể tạo correlated failure.

Do đó cần chọn trade-off theo workload, user impact, team capability và guarantee thực sự cần giữ.

Dùng chỉ số nào cho một backend cụ thể?

Mô tả load:

  • Requests/second theo endpoint và peak/average.
  • Read/write ratio.
  • Concurrent requests hoặc active users.
  • Payload size và data growth.
  • Cache hit rate.
  • Queue ingress rate, queue depth và fan-out nếu có.

Mô tả performance:

  • Throughput.
  • Client-observed response time p50, p95, p99.
  • Error rate và timeout rate để biết request nhanh nhưng sai không bị xem là tốt.
  • Resource saturation của CPU, memory, disk và network để tìm bottleneck.

Metric cuối cùng phải gắn với service contract. Ví dụ API tương tác có thể ưu tiên p95/p99 response time; batch job ưu tiên records/second và completion time; queue worker ưu tiên ingress/egress rate và backlog.

Viết lại bằng lời của tôi

Liên kết