65 ngày MongoDB
Ngày 1: Hệ thống phân tán và kiến trúc tổng thể của MongoDB
Lý thuyết:
Định lý CAP và PACELC trong bối cảnh của MongoDB. Phân tích sự đánh đổi giữa Consistency và Availability khi có Network Partition.
Kiến trúc Topology: Data flow đi qua
mongod(Data Node),mongos(Query Router), vàConfig Server(Metadata).Phân biệt luồng ghi (Write Path) và luồng đọc (Read Path) trong cụm Replica Set và Sharded Cluster.
Thực hành:
Sử dụng Docker để dựng một cụm Replica Set 3 node cục bộ (nếu dùng Mac/ARM, hãy pull image hỗ trợ
linux/arm64để tối ưu instruction set của CPU).Dùng
mongoshkết nối, chạyrs.status()vàdb.serverStatus(). Đọc và hiểu các thông số uptime, connections, và network bytes.
Bài tập:
Trình bày kịch bản phân mảnh mạng (split-brain) trong cụm 3 node. MongoDB xử lý các request đang chờ như thế nào dựa trên PACELC?
Vẽ lại sơ đồ luồng đi của một request từ khi chạm vào Load Balancer/Ingress của cụm Kubernetes đến khi ghi thành công vào Node Primary.
Ngày 2: Storage engine - WiredTiger (Phần 1: Disk I/O & Cấu trúc lưu trữ)
Lý thuyết:
Cấu trúc B-Tree dùng cho Documents và Indexes trong WiredTiger. Phân biệt với LSM Tree.
Cơ chế ghi dữ liệu (Write-Ahead Logging - Journaling):
Memory -> Journal (Disk) -> Data Files (Disk).Checkpoints: Quá trình snapshot lại state của data vào đĩa cứng (mặc định 60 giây hoặc 2GB journal).
MVCC (Multi-Version Concurrency Control) và Document-level locking hoạt động ra sao ở mức memory page.
Thực hành:
Tạo script (Python/Bash) đẩy một lượng lớn dữ liệu liên tục vào database.
Theo dõi Disk I/O, sử dụng lệnh
db.serverStatus().wiredTigerđể bóc tách các block thông tin vềlog(journaling) vàtransaction.
Bài tập:
Giải thích tham số
storage.journal.commitIntervalMs.Với một hệ thống xử lý giao dịch tài chính yêu cầu độ trễ thấp nhưng không được phép mất data khi crash, bạn sẽ cấu hình tham số này như thế nào? Phân tích trade-off.
Ngày 3: Storage engine - WiredTiger (Phần 2: Quản trị bộ nhớ & eviction)
Lý thuyết:
Công thức cấp phát RAM của WiredTiger Internal Cache:
MAX(0.5 * (RAM - 1GB), 256MB).Vai trò của OS Filesystem Cache đối với hiệu năng đọc.
Quá trình Eviction: Phân biệt Clean pages và Dirty pages. Thuật toán dọn dẹp bộ nhớ (LRU approximation).
Hiện tượng Cache Pressure: Điều gì xảy ra khi rate ghi vượt quá tốc độ đẩy data ra đĩa (Application threads yield).
Thực hành:
Cấu hình hard-limit RAM của container MongoDB ở mức 1GB (
--memory="1g").Chạy workload insert liên tục vượt quá 1GB. Theo dõi hành vi bộ nhớ qua
db.serverStatus().wiredTiger.cache.
Bài tập:
Bóc tách chỉ số
pages evicted by application threads. Nếu giá trị này tăng liên tục > 0, hệ thống đang gặp vấn đề gì?Thiết kế phương án cấp phát tài nguyên (CPU/RAM/Disk IOPS) cho một database node khi gặp hiện tượng OOM (Out Of Memory) do Cache Pressure.
Ngày 4: Phẫu thuật data types, BSON và cấu trúc byte
Lý thuyết:
Đặc tả BSON (Binary JSON) format. Cấu trúc Little-endian.
Giải phẫu ObjectId (12-byte):
4-byte timestamp+5-byte random+3-byte counter. Tại sao nó đáp ứng được tính phân tán mà không bị đụng độ?Định dạng lưu trữ số: Sự khác biệt ở tầng byte giữa
Double(Floating-point) vàDecimal128(IEEE 754-2008).Padding và overhead của việc đặt tên trường (field names) dài trong BSON.
Thực hành:
Tạo một document mô phỏng bản ghi chuyển tiền quốc tế, dùng
Decimal128cho số dư vàDatecho timestamp.Đo đạc kích thước byte thực tế của document bằng hàm
Object.bsonsize().
Bài tập:
So sánh chi phí lưu trữ (Storage Size) trên 100 triệu bản ghi nếu dùng tên trường
transaction_amount(18 bytes) so vớita(2 bytes).Tại sao tuyệt đối không dùng kiểu
Doublecho các phép tính cộng trừ số dư tài khoản? Nêu ví dụ sai số ở tầng binary.
Ngày 5: Vòng đời của một query & cấu trúc execution plan
Lý thuyết:
Luồng thực thi (Query Lifecycle):
Network Parse -> Authentication -> Query Planner -> Execution -> Fetch -> Response.Query Planner & Optimizer: Thuật toán chọn Plan. Cơ chế "chạy đua" (empirical testing) các plans để tìm ra Cached Plan tối ưu nhất.
Cơ chế Nhường quyền (Yielding): Tại sao engine nhả lock khi đang chờ Disk I/O hoặc scan quá lâu để các tác vụ khác chen ngang.
Thực hành:
Tạo collection với 1,000,000 documents.
Thực hiện truy vấn không index và có index. Đọc output của
.explain("executionStats").
Bài tập:
Bóc tách và giải thích ý nghĩa của các thông số:
executionTimeMillis,totalDocsExamined,nReturned.Phân tích chỉ số
worksvàyields. Truy vấn của bạn đã phải nhường quyền bao nhiêu lần? Tác động của nó đến Thread Pool của database là gì?
Ngày 6: Mổ xẻ indexing internals và phân mảnh memory page
Lý thuyết:
Cách B-Tree indexes ánh xạ xuống các memory pages.
Right-balanced indexes (ObjectId, Date) so với random distribution indexes (UUIDv4, Hash).
Index Prefix Compression trong WiredTiger: Cách MongoDB nén các index keys có tiền tố giống nhau.
Hiện tượng Page Split và Index Bloat khi insert dữ liệu không tuần tự.
Thực hành:
Tạo 2 collections: một sử dụng
_idmặc định (ObjectId), một sử dụng_idlà chuỗi UUID ngẫu nhiên.Insert 500,000 bản ghi vào mỗi collection. So sánh kích thước index qua
db.collection.stats().
Bài tập:
Tại sao UUID ngẫu nhiên gây tốn RAM (Cache eviction liên tục) và Disk I/O hơn rất nhiều so với ObjectId khi hệ thống scale lên hàng trăm triệu bản ghi?
Đề xuất cách thiết kế custom ID kết hợp giữa Timestamp và Random string để tối ưu B-Tree insert.
Ngày 7: Giao thức mạng (Wire Protocol) và connection pooling
Lý thuyết:
MongoDB Wire Protocol hoạt động trên TCP/IP. Cấu trúc của OP_MSG.
Kiến trúc Connection Pool của Driver: Các trạng thái kết nối (Pending, In Use, Idle).
Các tham số sinh tử:
maxPoolSize,minPoolSize,waitQueueTimeoutMS,maxIdleTimeMS.Thread per connection model của
mongodvà nguy cơ cạn kiệt tài nguyên (Thread exhaustion).
Thực hành:
Cấu hình một ứng dụng (Python/Node.js) kết nối đến MongoDB. Điều chỉnh
maxPoolSize=2.Bắn đồng thời 10 queries tốn thời gian (ví dụ query sử dụng
$wherehoặc count full table) và quan sát hiện tượng Connection Timeout ở phía client.
Bài tập:
Tính toán lý thuyết số lượng connection tối đa dội vào database nếu kiến trúc backend của bạn gồm 20 microservices, mỗi service chạy 5 pods, mỗi pod set
maxPoolSize = 100.Nếu backend liên tục báo lỗi
MongoTimeoutError: Server selection timed out, bạn sẽ trace lỗi từ tầng Network, Driver đến Database như thế nào để khoanh vùng nguyên nhân?
Ngày 8: Kiến trúc Aggregation Engine & Quản trị Memory (Streaming vs. Blocking)
Aggregation không phải là một phép toán đơn lẻ, nó là một dây chuyền (pipeline) xử lý BSON document. Cần hiểu rõ stage nào tiêu tốn RAM và stage nào an toàn.
Lý thuyết:
Streaming Stages vs. Blocking Stages:
Streaming (
\(match,\)project,\(addFields,\)unwind): Xử lý document từng cái một (document-at-a-time). Dùng rất ít RAM.Blocking (
\(group,\)sort,\(bucket,\)facet): Phải giữ lại toàn bộ hoặc một phần dữ liệu trong RAM trước khi có thể đẩy output sang stage tiếp theo.The 100MB RAM Limit: Mặc định, mỗi Blocking stage chỉ được phép dùng tối đa 100MB RAM. Nếu vượt quá, request sẽ bị kill với lỗi OOM.
Spill to Disk (
allowDiskUse): Cơ chế mượn đĩa cứng làm swap tạm thời khi vượt giới hạn 100MB. Đánh đổi (trade-off) khủng khiếp về Disk I/O và Latency.
Thực hành:
Chạy một pipeline có chứa
$grouptrên một collection khoảng 5 triệu bản ghi mà không có Index hỗ trợ.Bật/tắt tùy chọn
allowDiskUse: truevà quan sát Disk I/O Write thông qua lệnh giám sát hệ điều hành (nhưiostathoặc Activity Monitor/top).
Bài tập:
Trong một tác vụ đối soát (reconciliation) giao dịch cuối ngày (EOD), bạn buộc phải group hàng triệu bản ghi. Đề xuất kiến trúc hạ tầng RAM/Disk và cấu hình Aggregation để tránh việc hệ thống online bị ảnh hưởng khi job EOD này chạy.
Giải thích tại sao
\(limitlại giúp biến một Blocking stage như\)sorttrở thành Top-K Streaming stage (tối ưu bộ nhớ)?
Ngày 9: Aggregation Optimizer
Lý thuyết:
Pipeline Rewriting (Sequence Optimization): Trình tối ưu hóa tự động di chuyển các stage. Ví dụ: Đẩy
\(matchlên trước\)sort, gộp (coalesce) nhiều\(projectliền kề thành một, gộp\)skipvà$limit.Index Pushdown: Trong Pipeline, Index chỉ được sử dụng nếu
\(matchhoặc\)sortnằm ở ĐẦU pipeline (hoặc ngay sau các stage không làm thay đổi document như$projectkhông sinh trường mới).Phân tích output của
.explain("executionStats")cho Aggregation, tập trung vào blockparsedPipeline.
Thực hành:
Viết một pipeline cố tình đặt sai thứ tự:
db.coll.aggregate([ {\(project: ...}, {\)match: ...}, {$sort: ...} ]).Chạy hàm
.explain()và đọc JSON output ở phầnparsedPipeline.
Bài tập:
Phân tích cách MongoDB biến đổi pipeline sai thứ tự của bạn ở trên. Trình tối ưu hóa đã kéo
$matchlên vị trí nào?Nếu bạn có một
\(unwindnằm TRƯỚC một\)match, Query Optimizer có thể tự động đẩy\(matchlên trước\)unwindđược không? (Gợi ý: Tìm hiểu điều kiện hoán vị của optimizer).
Ngày 10: Chi phí ẩn của Arrays: $unwind vs. Array Operators
Việc bóc tách mảng (array) là bài toán kinh điển trong NoSQL, nhưng lạm dụng $unwind là nguyên nhân số một gây bùng nổ CPU.
Lý thuyết:
Hiệu ứng Cartesian Explosion (Bùng nổ tổ hợp): Khi
\(unwindmột mảng có 100 phần tử, 1 document gốc trong RAM bị nhân bản thành 100 documents. Nếu pipeline tiếp tục\)unwindmột mảng khác, số lượng documents tăng theo cấp số nhân, vắt kiệt CPU/RAM.Aggregation Expression Operators: Sự thay thế hoàn hảo cho
\(unwindkhi chỉ cần tính toán trong nội bộ một document:\)filter,\(map,\)reduce.
Thực hành:
Chạy hai pipeline để tính tổng phí giao dịch từ một mảng
feesnằm bên trong documentticket.Cách 1:
\(unwindmảngfees->\)grouplại bằng_idgốc để tính tổng.Cách 2: Sử dụng thuần
\(reduceđể cộng dồn trực tiếp trong\)project.
Bài tập:
So sánh
executionTimeMilliscủa hai cách trên qua.explain(). Cách 2 nhanh hơn bao nhiêu phần trăm?Thiết kế rule Validation cho Team Code: Khi nào BẮT BUỘC phải dùng
\(unwindvà khi nào TUYỆT ĐỐI CẤM dùng\)unwind?
Ngày 11: Internals của $lookup (JOIN) & Thực thi phân tán
Cách MongoDB thực hiện JOIN (Left Outer Join) và bài toán đau đầu khi đưa nó lên kiến trúc Sharded Cluster.
Lý thuyết:
Uncorrelated Subquery vs. Correlated Subquery:
Cú pháp
$lookupcơ bản (localField, foreignField) là Uncorrelated.Cú pháp
$lookupdùngletvàpipelinelà Correlated (cho phép thực hiện các phép JOIN phức tạp hơn, ví dụ có điều kiệnAND/ORvới bảng đích).Cơ chế thực thi trên Sharded Cluster: Từ MongoDB 5.1+,
$lookupcó thể query vào các Sharded Collection. Phân tích quá trìnhmongosbroadcast truy vấn xuống các shards (Scatter-Gather) và cost network latency.Tầm quan trọng tuyệt đối của Index trên
foreignField.
Thực hành:
Dựng hai collection:
usersvàtransactions.Viết một Correlated
\(lookupđể lấy ra User kèm theo chỉ 3 giao dịch chuyển tiền ngoại tệ (USD) gần nhất của họ (sử dụnglet,\)matchtheo currency, và$limit: 3ngay trong sub-pipeline).
Bài tập:
Giải thích sự khác biệt về Memory tiêu thụ giữa việc dùng
\(lookuplấy toàn bộ transactions rồi mới\)sliceở ngoài, so với việc dùng\(limitngay trong sub-pipeline của\)lookup.Đánh giá rủi ro về độ trễ (latency) nếu collection
usersvàtransactionsnằm trên 2 Shards hoàn toàn khác biệt về mặt địa lý.
Ngày 12: Window Functions ($setWindowFields)
Tính năng xoay chuyển cục diện của MongoDB (từ bản 5.0), cho phép NoSQL tính toán dữ liệu chuỗi thời gian, tài chính ngang ngửa với SQL truyền thống.
Lý thuyết:
Cơ chế Windowing: Khái niệm Partitioning (chia cụm), Sorting (sắp xếp trong cụm) và Window Frame (cửa sổ trượt) mà không cần phải
$grouplàm mất cấu trúc document ban đầu.Ứng dụng để giải quyết các bài toán khó: Tính số dư lũy kế (Running Total), Trung bình động (Moving Average), Tính thứ hạng (Ranking).
Thực hành:
Tạo một tập dữ liệu biến động số dư tài khoản theo từng ngày.
Sử dụng
$setWindowFieldsvới window frame[ -6, "current" ]để tính "Trung bình lượng tiền chuyển trong 7 ngày gần nhất" (7-day Moving Average) cho mỗi document.
Bài tập:
Không dùng
\(setWindowFields, hãy thử phác thảo logic để tính số dư lũy kế (Running Total) bằng các aggregation stages cũ (như\)group,\(push, rồi quét mảng). Sau đó so sánh độ phức tạp (Complexity) và hiệu năng với\)setWindowFields.Phân tích tại sao việc đưa logic tính toán Window Functions xuống tầng Database (MongoDB) lại tốt hơn việc kéo (fetch) toàn bộ dữ liệu lên tầng Application (Nodejs/Python) để dùng vòng lặp
fortính toán?
Ngày 13: Cốt lõi ACID, MVCC và Snapshot Isolation
MongoDB không dùng cơ chế Table Lock hay Row Lock truyền thống của SQL, mà sử dụng MVCC. Nếu không hiểu Snapshot Isolation, bạn sẽ không xử lý được lỗi WriteConflictException kinh điển.
Lý thuyết:
MVCC (Multi-Version Concurrency Control) trong WiredTiger: Cách engine giữ nhiều phiên bản của cùng một document trong RAM. Khi một Transaction bắt đầu, nó tạo ra một Point-in-time Snapshot (bức ảnh chụp trạng thái dữ liệu).
Write Conflicts (Xung đột ghi): Nếu hai transactions cùng cố gắng sửa một document, Transaction nào commit sau sẽ bị hủy (Abort) với lỗi
WriteConflictExceptionthay vì bị block chờ đợi như trong SQL.Read Concern
snapshot: Đảm bảo mọi thao tác đọc trong một giao dịch đều nhìn thấy một dữ liệu nhất quán từ đầu đến cuối, ngăn chặn hiện tượng Phantom Read.
Thực hành:
Mở 2 terminal
mongoshsong song. Bắt đầu Transaction 1 (chưa commit) điểu chỉnh số dư của tài khoản A.Ở terminal 2, chạy một lệnh update trực tiếp (không dùng transaction) vào chính tài khoản A.
Quay lại terminal 1 thực hiện commit và phân tích stack trace của lỗi văng ra.
Bài tập:
Khi thiết kế luồng chuyển tiền, nếu xảy ra
WriteConflictException, tầng Application (Node.js/Java) của bạn phải làm gì? Viết giả mã (pseudocode) mô phỏng cơ chế Retry với Exponential Backoff.Giải thích tại sao việc giữ một Transaction mở (open) quá lâu (ví dụ đang chờ gọi API ngoại mạng) lại gây phình to RAM của WiredTiger (Gợi ý: Garbage Collection của MVCC).
Ngày 14: Multi-Document Transactions: Giới hạn RAM & Oplog
Giao dịch nhiều documents không phải là "viên đạn bạc". Nó tốn kém tài nguyên gấp nhiều lần so với ghi thông thường.
Lý thuyết:
Transaction Buffer (In-Memory): Mọi thao tác ghi trong một giao dịch không được ghi trực tiếp xuống đĩa ngay mà bị giữ trong RAM.
Giới hạn 16MB (Oplog Entry Limit): Mặc dù các bản MongoDB mới đã gỡ bỏ phần nào, nhưng bản chất khi commit, toàn bộ thay đổi của Transaction sẽ được đóng gói thành một mảng
applyOpsghi vào Oplog. Nếu giao dịch sửa quá nhiều documents, nó sẽ phá vỡ giới hạn BSON size (16MB).Tham số
transactionLifetimeLimitSeconds: Mặc định MongoDB tự động kill mọi transaction chạy quá 60 giây. Tại sao lại có con số này?
Thực hành:
Cấu hình lại
transactionLifetimeLimitSecondsxuống còn 5 giây.Dùng Driver (Python/Node.js) mở hàm
withTransaction. Trong logic transaction, cố tình chosleep(6).Quan sát lỗi
NoSuchTransactionbị bắn ra khi cố gắng commit.
Bài tập:
Một team Dev đề xuất đưa toàn bộ tiến trình: Trừ tiền KH A -> Đợi Call API sang Swift/Ripple (mất 2s) -> Cộng tiền KH B vào chung MỘT transaction. Với góc độ Solution Architect, bạn hãy bác bỏ thiết kế này và đưa ra pattern thay thế (Saga Pattern hoặc Outbox Pattern).
Hạn chế lớn nhất của việc lạm dụng Multi-Document Transaction đối với tốc độ Replication (đồng bộ sang Node Secondary) là gì?
Ngày 15: Distributed Transactions & Two-Phase Commit (2PC) trên Sharded Cluster
Khi giao dịch vượt ra khỏi ranh giới của một Replica Set và trải dài trên nhiều Shards, độ phức tạp tăng lên theo cấp số nhân.
Lý thuyết:
Thuật toán Two-Phase Commit (2PC): Router
mongosđóng vai trò là Transaction Coordinator. Nó điều phối 2 pha: Prepare Phase (hỏi các Shards xem có sẵn sàng commit không) và Commit Phase (chốt hạ).Latency Penalty: Phân tích đường đi của mạng. Một giao dịch phân tán cần ít nhất bao nhiêu network round-trips giữa
mongos, Config Servers và các Shards?Stale Config & Phantoms: Điều gì xảy ra với một Distributed Transaction nếu đúng lúc đó hệ thống đang diễn ra Chunk Migration (di chuyển dữ liệu giữa các Shard)?
Thực hành:
Khởi động cụm Sharded Cluster (ít nhất 2 Shards). Shard collection
transactionstheouser_id.Bắn một Transaction chuyển tiền từ
user_id: 1(nằm ở Shard A) sanguser_id: 99(nằm ở Shard B).Dùng
.explain()hoặc profiler để đo độ trễ (latency).
Bài tập:
Thiết kế Shard Key như thế nào để 95% các giao dịch chuyển tiền đều rơi vào cục bộ một Shard (Single-Shard Transaction), và chỉ 5% bị rơi vào Distributed Transaction?
Vẽ sơ đồ giải thích kịch bản: Ở pha Commit Phase, Coordinator báo chốt hạ, nhưng một Shard đột ngột mất mạng trước khi nhận được lệnh. MongoDB giải quyết tính toàn vẹn (Consistency) trong tình huống này ra sao?
Ngày 16: Tính Lũy đẳng (Idempotency) với Retryable Writes & Reads
Một hệ thống tài chính không bao giờ được phép trừ tiền hai lần chỉ vì chập chờn mạng. Đây là lúc Logical Sessions phát huy sức mạnh.
Lý thuyết:
Logical Session ID (LSID) & Transaction Number (
txnNumber): Cách MongoDB theo dõi độc nhất từng request.Cơ chế Retryable Writes: Khi Driver gửi lệnh trừ 100\(, mất mạng, Driver tự động retry. Làm sao
mongodbiết lệnh này đã được thực thi rồi để không trừ 200\)? (Sử dụng bảng băm internal để lưu kết quả của cáctxnNumberđã thành công).Retryable Reads: Làm sao để không bị văng lỗi khi đang đọc dữ liệu mà Node Primary chết và cụm đang tiến hành failover?
Thực hành:
Viết một đoạn code insert một bản ghi quan trọng (có bật
retryWrites=truetrong chuỗi URI).Cắm debug breakpoint ở Driver ngay TRƯỚC khi nhận response từ server, sau đó tắt nóng
mongodPrimary để ép hệ thống bầu Primary mới. Code tự động retry như thế nào?
Bài tập:
Retryable Writes có hỗ trợ cho lệnh
updateManyhoặcdeleteManykhông? Giải thích nguyên lý tại tầng Internals.Trong trường hợp hệ thống mạng bị chập chờn liên tục, Retryable Writes có làm tăng kích thước của bộ nhớ RAM trên server không? (Gợi ý: Tìm hiểu thời gian sống mặc định của thông tin LSID trên server).
Ngày 17: Causal Consistency & Lock Contention Anti-Patterns (Level Architect)
Không chỉ có ACID, chúng ta cần dữ liệu đọc ra phải hợp lý về mặt nhân quả, và hệ thống phải tránh được nạn nghẽn cổ chai do khóa (locks).
Lý thuyết:
Causal Consistency (Đồng nhất nhân quả): "Read your own writes". Đảm bảo rằng nếu một user vừa nạp tiền thành công, lần reload trang tiếp theo họ (hoặc một tiến trình khác) CHẮC CHẮN phải thấy số dư mới, dù truy vấn đọc bị đẩy sang một Node Secondary đang bị trễ (Replication Lag).
ClusterTime & OperationTime: Cách MongoDB truyền thời gian logic (Lamport Clock) qua lại giữa Client và Server để duy trì nhân quả.
Transaction Anti-Patterns: * Cập nhật chung một "bản ghi cổ chai" (Ví dụ: 10,000 users cùng lúc update vào một document tổng kết doanh thu).
- Read-Modify-Write cycle quá dài.
Thực hành:
Thiết lập session với
causalConsistency: true.Cố tình tạo độ trễ đồng bộ (replication lag) trên Node Secondary bằng cách pause process.
Ghi dữ liệu vào Primary, sau đó dùng session đó ép đọc từ Secondary. Quan sát hành vi bị block (chờ đợi) cho đến khi Secondary đuổi kịp OperationTime của thao tác ghi trước đó.
Bài tập:
Trong một tác vụ chạy batch ban đêm (EOD), bạn dùng một giao dịch để duyệt và cập nhật 5 triệu bản ghi. Hãy chứng minh bằng lý thuyết kỹ thuật tại sao cách làm này sẽ đánh sập Node Primary ngay lập tức.
Bất kỳ một kiến trúc sư nào cũng phải chuẩn bị tài liệu rủi ro. Viết một danh sách (checklist) 5 điều kiện BẮT BUỘC để một developer trong team của bạn được quyền mở cú pháp
withTransactionvào code Production.
Ngày 18: Giải phẫu Sharding Topology & Metadata (CSRS)
Nhiều người nghĩ mongos tự biết dữ liệu ở đâu, nhưng thực tế nó hoàn toàn phụ thuộc vào Config Servers. Hiểu Config Servers là hiểu bộ não của cụm.
Lý thuyết:
Config Server Replica Set (CSRS): Nơi lưu trữ "Bản đồ" (Metadata). Bản đồ này map các khoảng giá trị (ranges) của Shard Key vào các Chunks, và map Chunks vào các Shards.
Cơ chế Caching của
mongos:mongoskhông query Config Server mỗi lần nhận request. Nó cache metadata trên RAM.Stale Config Error (Lỗi cấu hình cũ): Điều gì xảy ra khi một Chunk vừa bị chuyển (migrate) sang Shard khác, nhưng
mongosvẫn dùng cache cũ và trỏ nhầm Shard? Cơ chế server tự động ném lỗiStaleConfigđể épmongoscập nhật lại cache.
Thực hành:
Dựng một Sharded Cluster hoàn chỉnh trên local (Docker) gồm: 1 Replica Set làm CSRS, 2 Replica Sets làm Shards, và 1 node
mongos.Kết nối thẳng vào Config Server (không qua
mongos), query databaseconfig, đọc các collectionsconfig.shardsvàconfig.chunksđể xem hình thù của Metadata dưới dạng BSON.
Bài tập:
Kịch bản thảm họa: Toàn bộ cụm Config Servers (CSRS) bị sập mạng. Ứng dụng của bạn (kết nối qua
mongos) sẽ bị ảnh hưởng như thế nào? Phân tích xem các thao tác: Đọc, Ghi, và Chunk Split/Migration cái nào còn hoạt động, cái nào chết?Đề xuất kiến trúc triển khai CSRS đa vùng (Multi-Region) để đảm bảo High Availability cho Metadata.
Ngày 19: Toán học Shard Key & Khủng hoảng Jumbo Chunks
Sai lầm chí mạng nhất của mọi hệ thống MongoDB là chọn sai Shard Key. Đổi Shard Key (trước bản 5.0) là bất khả thi, và từ 5.0 trở đi thì cực kỳ tốn kém.
Lý thuyết:
3 Yếu tố cốt lõi của Shard Key: Cardinality (Tính đa dạng), Frequency (Tần suất lặp), và Rate of Change (Tốc độ thay đổi tăng dần).
Ranged vs. Hashed dưới góc độ B-Tree: Hashed Sharding băm (MD5) giá trị đầu vào để phân bổ đều ghi (Write), nhưng giết chết hiệu năng của truy vấn dải (Range Query). Ranged Sharding giữ nguyên thứ tự B-Tree, tốt cho Range Query nhưng dễ gây Hotspot (nghẽn ghi).
Jumbo Chunks: Khi một Chunk vượt quá 64MB (mặc định) HOẶC chứa số lượng documents vượt mức giới hạn, nhưng MongoDB không thể cắt nó ra (Split) vì tất cả documents trong Chunk đó có CÙNG MỘT giá trị Shard Key. Hệ quả: Chunk này vĩnh viễn kẹt lại ở 1 Shard.
Thực hành:
Tạo một collection. Chọn Shard Key là
{ branch_id: 1 }.Cố tình insert 500,000 documents có cùng
branch_id = "HANOI_01".Kiểm tra trạng thái chunk. Dùng lệnh
sh.status()để tìm cờjumbo(hoặc xem log). Cố gắng ép Balancer di chuyển chunk này và quan sát lỗi.
Bài tập:
Thiết kế Shard Key cho core collection
transactions. Hệ thống yêu cầu: (1) Tránh Hotspot khi insert hàng loạt giao dịch cuối ngày. (2) Phải query siêu nhanh lịch sử giao dịch của mộtuser_id. (Gợi ý: Cân nhắc Compound Hashed Shard Key).Giải quyết hậu quả: Hệ thống hiện tại đang bị 5 Jumbo Chunks chặn đứng Balancer. Phác thảo quy trình xử lý sự cố để gỡ rối tình huống này trên Production.
Ngày 20: Pre-splitting & Balancer Migration Internals
Đừng để MongoDB tự động cân bằng (auto-balance) vào giờ cao điểm. Quá trình Migration tốn rất nhiều I/O và Network.
Lý thuyết:
Vòng đời của một Chunk: Tăng kích thước -> Đạt ngưỡng (Threshold) -> Split (Cắt đôi) -> Migration (Di chuyển bởi Balancer).
Cơ chế Chunk Migration (Dưới vỏ bọc): Khi Balancer chuyển Chunk A từ Shard 1 sang Shard 2. Các bước:
Bắt đầu Copy->Catch up Oplog (để đồng bộ các thay đổi xảy ra trong lúc copy)->Commit (đổi Metadata trên CSRS)->Xóa data cũ trên Shard 1 (Thường là chạy ngầm).Write Amplification (Khuếch đại ghi): Quá trình chuyển chunk khiến 1 thao tác ghi ban đầu biến thành hàng loạt thao tác ghi/xóa ở background.
Thực hành:
Hạ cấu hình
chunkSizexuống 1MB để dễ kích hoạt cơ chế chia chunk.Viết script đẩy data liên tục. Mở log của
mongodtrên Shard Primary để trace quá trìnhsplitChunkvàmigrateChunkdiễn ra real-time.
Bài tập:
Bài toán Bulk Insert: Bạn cần import 1 tỷ bản ghi từ hệ thống core cũ vào một Sharded Collection mới tinh của MongoDB. Nếu làm bình thường, 100% traffic sẽ dội vào 1 Shard duy nhất. Trình bày kỹ thuật Pre-splitting (Cắt sẵn chunk và phân bổ thủ công trước khi import) để song song hóa quá trình ghi ra toàn bộ các Shards.
Thiết lập cấu hình Balancer Window để Balancer chỉ được phép chạy từ 2:00 AM đến 4:00 AM. Tại sao SA luôn khuyên tắt Balancer trong giờ giao dịch hành chính?
Ngày 21: Data Sovereignty, Zones & Tiered Storage
Lý thuyết :
Zone/Tag Aware Sharding: Tính năng cho phép ghim (pin) một dải Shard Key cụ thể vào các Shards cụ thể.
Data Sovereignty (Chủ quyền dữ liệu): Đảm bảo giao dịch của khách hàng EU phải nằm ở Datacenter EU, giao dịch của Việt Nam phải nằm ở VN (vì lý do pháp lý/compliance).
Tiered Storage (Hot-Warm-Cold Data): Shard 1 & 2 dùng ổ NVMe siêu tốc cho dữ liệu giao dịch của năm nay. Shard 3 & 4 dùng ổ HDD rẻ tiền cho dữ liệu lưu trữ (Archive) của 5 năm trước.
Thực hành:
Gắn tags cho các shards: Shard 1 tag
HOT, Shard 2 tagCOLD.Cập nhật zone ranges cho collection
transactions: Dữ liệu có trườngcreated_attrong vòng 1 năm trỏ vào zoneHOT, dữ liệu cũ hơn 1 năm trỏ vào zoneCOLD.Đẩy dữ liệu cũ vào và xem Balancer tự động "dọn nhà" cho dữ liệu sang Shard COLD.
Bài tập:
Thiết kế chiến lược lưu trữ (Storage Architecture): Các giao dịch quá hạn 12 tháng phải được tự động chuyển sang Shard lưu trữ chi phí thấp, nhưng tầng Application không được thay đổi bất kỳ dòng code nào. Trình bày luồng hoạt động của Balancer.
Nếu một Shard được gán Zone "VN" bị đầy (Disk Space > 90%), Balancer có tự động chuyển bớt chunk "VN" sang Shard gán Zone "US" đang trống để cứu hệ thống không? Phân tích hành vi ưu tiên của Balancer.
Ngày 22: Query Routing - Thảm họa Scatter-Gather & Orphaned Docs
Tốc độ của Sharded Cluster có thể... chậm hơn cả Standalone nếu truy vấn bị định tuyến sai (Routing Anti-patterns).
Lý thuyết:
Targeted Query (Định tuyến trúng đích): Truy vấn CÓ chứa Shard Key.
mongostra bản đồ và phi thẳng đến đúng 1 Shard. (O(1) routing).Scatter-Gather Query (Rải - Gom): Truy vấn KHÔNG có Shard Key.
mongosbuộc phải gửi câu lệnh tới TẤT CẢ các shards. Đợi tất cả trả về, sau đómongosđóng vai trò merge kết quả.Chi phí ẩn của
$sort: Trong Scatter-Gather,mongosphải tải kết quả từ các shards lên RAM của chính nó để làm thao tác Merge-Sort, cực kỳ dễ gây OOM trênmongos.Orphaned Documents: Những bản ghi bị "bỏ quên" lại trên Shard cũ sau quá trình Migration fail hoặc timeout.
Thực hành:
Tạo collection sharded, chèn 1 triệu bản ghi.
Chạy
.explain()cho 2 trường hợp: Query kẹp Shard Key và Query không Shard Key. Đọc phầnshardReadstrong output JSON để xem có bao nhiêu Shards bị hit.Sử dụng lệnh
cleanUpOrphanedtrên các shards.
Bài tập:
Bài toán hack não: Bạn có một cluster 50 Shards. Bảng
logsphân mảnh theouser_id. Bạn chạy câu lệnh:db.logs.find({ status: "FAILED" }).sort({ timestamp: -1 }).limit(10). (Truy vấn không chứa shard key). Hỏi: Routermongossẽ kéo TỔNG CỘNG bao nhiêu bản ghi qua mạng từ 50 Shards lên RAM của nó để lấy ra được đúng Top 10 toàn cục? Giải thích rủi ro.Đề xuất phương án thiết kế Index và Query để triệt tiêu hoàn toàn hiện tượng Scatter-Gather trong các màn hình Dashboard liệt kê giao dịch của Admin.
Ngày 23: Giải phẫu Oplog (Operations Log) & Tính Lũy Đẳng (Idempotency)
Oplog là trái tim của Replication. Hiểu cách Oplog lưu trữ dữ liệu là chìa khóa để debug các lỗi đồng bộ nghẽn cổ chai.
Lý thuyết:
Cấu trúc Capped Collection:
local.oplog.rshoạt động như một vòng ring buffer. Tại sao nó lại không có Index (ngoại trừ trên trườngtstimestamp) và ghi cực nhanh?Oplog Entry Format: Phân tích các trường bí ẩn trong một bản ghi Oplog:
ts(Timestamp),t(Term),h(Hash),op(Operation type: i, u, d, c, n),ns(Namespace),ovào2(Dữ liệu).Idempotent Operations (Tính Lũy Đẳng): Tại sao Oplog không bao giờ lưu lệnh
\(incmà tự động chuyển đổi (transform) nó thành lệnh\)setvới giá trị tuyệt đối trước khi replicate? Nguyên lý áp dụng lại (replay) oplog nhiều lần mà không làm sai lệch dữ liệu.
Thực hành:
Dựng Replica Set 3 node cục bộ (đảm bảo pull image
mongo:latestkiến trúclinux/arm64cho môi trường Mac M-series).Thực hiện một lệnh update sử dụng
$inc: { balance: 50 }trên Node Primary.Kết nối thẳng vào database
localcủa Primary, query bảngoplog.rsvà trích xuất BSON document của thao tác vừa rồi.
Bài tập:
So sánh chi phí lưu trữ và băng thông mạng (network bandwidth) giữa một lệnh
updateManyảnh hưởng 10,000 bản ghi trên Primary so với những gì thực sự được ghi vào Oplog.Nếu
oplog sizeđược cấu hình là 50GB. Làm sao để tính toán được "Oplog Window" (Thời gian cửa sổ oplog - lượng thời gian tối đa một Node Secondary có thể bị ngắt kết nối mà vẫn có thể đuổi kịp dữ liệu khi online lại)?
Ngày 24: Thuật toán đồng thuận Raft & Bầu chọn Primary (Election Internals)
MongoDB sử dụng một biến thể của thuật toán Raft để đảm bảo tính nhất quán (Consensus) trong phân tán.
Lý thuyết:
Raft Algorithm: Khái niệm Term (Nhiệm kỳ), Heartbeat (Nhịp tim 2 giây/lần), và Election Timeout (thời gian chờ mặc định 10 giây).
Giai đoạn bầu chọn (Election Process):
Catchup TakeovervsPriority Takeover. Làm thế nào các node so sánh độ "tươi" (freshness) của Oplog thông quaopTimeđể quyết định ai có quyền lên làm Primary?Veto Power (Quyền phủ quyết): Trong trường hợp nào một Secondary từ chối vote cho một ứng cử viên dù ứng cử viên đó có Priority cao nhất?
Thực hành:
Sử dụng CNI Cilium hoặc Network Policies trong Kubernetes (chạy qua minikube/kind) để giả lập việc cắt đứt mạng (Network Partition) ngăn cách Node Primary hiện tại với 2 Node Secondary còn lại.
Đọc real-time log của cụm để quan sát quá trình một Secondary tự động trigger election sau 10 giây không nhận được Heartbeat.
Bài tập:
Kịch bản Split-Brain: Mạng bị chia làm 2 nửa. Nửa A có Node Primary cũ và 1 Arbiter. Nửa B có 2 Node Secondary. Chuyện gì sẽ xảy ra? Cụm nào sẽ tiếp tục nhận được write traffic?
Tại sao thiết kế hạ tầng Production luôn khuyên cấu hình số lượng voting node là số lẻ (3, 5, 7)? Việc triển khai cụm 4 node có tác hại gì tới thuật toán Raft?
Ngày 25: Phân tích thảm họa Rollback & Tuning Read/Write Concerns
Lý thuyết:
Cơ chế Rollback: Khi Node Primary cũ bị rớt mạng trước khi kịp đồng bộ Oplog sang Secondary, một Secondary mới được bầu lên. Khi Primary cũ sống lại, nó phát hiện lịch sử (Oplog divergence) bị lệch và buộc phải vứt bỏ (rollback) các dữ liệu chưa từng được replicate.
Write Concern
w: majority&j: true: Tấm khiên thép bảo vệ sổ cái tài chính. Tại saow: 1kết hợpj: falselại mang rủi ro mất dữ liệu cực cao.Read Concern
linearizable: Mức độ đọc khắt khe nhất, yêu cầu Primary phải ping một round-trip tới toàn bộ đa số các node trước khi trả kết quả cho client để xác nhận nó vẫn còn là Primary (chống Phantom Read do Stale Primary).
Thực hành:
Tạo cấu hình ép buộc Rollback: Chặn đồng bộ mạng giữa Primary và Secondary. Insert 10 bản ghi vào Primary (sử dụng
w:1).Tắt Primary (kill process). Bỏ chặn mạng để Secondary lên ngôi. Chèn thêm dữ liệu vào Primary mới.
Bật lại node cũ, quan sát thư mục
rollbacktrong đường dẫn data của MongoDB.
Bài tập:
Trong ứng dụng, nếu một transaction ghi bằng
w:1bị rơi vào file BSON trong thư mụcrollback. Là một Platform Engineer, quy trình (SOP) của bạn để khôi phục (replay) dữ liệu từ file BSON này bằng công cụbsondumpvàmongorestorelà gì?Giải thích sự đánh đổi về Latency (độ trễ) giữa
w: majorityso vớiw: 1. Làm sao để tối ưu Disk I/O trên toàn bộ cụm khi bậtj: true(Journaling)?
Ngày 26: Data Center Awareness, Custom Tags & Disaster Recovery
Kiến trúc High Availability không chỉ là chạy nhiều máy, mà là cách phân bổ các máy đó qua các Availability Zones (AZ) và xử lý rủi ro từ con người (Human Error).
Lý thuyết:
Cross-Region Replication: Chống chịu thảm họa cấp độ Datacenter (Ví dụ: DC chính ở Hà Nội sập, DC dự phòng ở TP.HCM phải tự động Takeover).
Replica Set Tags: Đánh tag cho các node (
{ "region": "Hanoi", "disk": "NVMe" }) để thiết lập các rule Read Preference tuỳ chỉnh (Ví dụ: Ứng dụng Backend tại Hà Nội chỉ được phép đọc dữ liệu từ các node mang tag Hà Nội để tối ưu network latency).Delayed Replica Set Members: Node "du hành thời gian" (ví dụ delay 1 tiếng). Vũ khí tối thượng chống lại câu lệnh
db.dropDatabase()hoặcdeleteMany({})do lỗi con người hoặc tấn công Ransomware.
Thực hành:
Cấu hình lại cụm 3 node: Thêm tag
{ location: "DC1" }cho Node 1 và 2, tag{ location: "DC2" }cho Node 3.Setup Node 3 thành Delayed Node (chậm hơn Primary 1 giờ) bằng thuộc tính
slaveDelay. Setpriority: 0vàhidden: true.
Bài tập:
Thiết kế kiến trúc cụm 5 nodes phân bổ giữa Hà Nội và Hồ Chí Minh. Làm sao để đảm bảo: Nếu đứt cáp quang biển hoặc cáp trục Bắc Nam, Hà Nội vẫn hoạt động bình thường, nhưng Hồ Chí Minh chỉ được phép Read-Only?
Phân tích rủi ro của việc sử dụng Arbiter (Node chỉ vote, không chứa data). Tại sao trong các Cluster xử lý dữ liệu lõi, các chuyên gia luôn khuyến cáo tuyệt đối không dùng Arbiter?
Ngày 27: Change Streams & Kiến trúc Event-Driven (CDC)
Chuyển từ tư duy Polling (quét database liên tục) sang Event-Driven (Lắng nghe sự kiện) là bước tiến lớn nhất để tối ưu tài nguyên cho các hệ thống vi dịch vụ (Microservices).
Lý thuyết:
Change Streams Architecture: Bản chất là một wrapper (bọc ngoài) Oplog, cho phép Client nhận các event thay đổi dữ liệu (insert, update, delete, replace) real-time với chuẩn API.
Resume Token: Làm sao để khi một Microservice (như Notification Service) bị crash, lúc khởi động lại nó biết chính xác cần đọc tiếp sự kiện từ đâu trong Oplog mà không bị sót hoặc trùng lặp (Exactly-once semantics)?
Full Document Lookup: Đối với lệnh update, mặc định Change Streams chỉ trả về phần field bị sửa (Delta). Tùy chọn
updateLookupgiúp fetch toàn bộ document, nhưng chi phí tài nguyên phải trả là gì?
Thực hành:
Viết một script Node.js hoặc Python sử dụng
.watch()để theo dõi collectiontransactions.Đưa thêm Aggregation Pipeline vào Change Streams để "filter" sự kiện: Chỉ bắn thông báo (trigger event) khi trường
statuscủa giao dịch chuyển từPENDINGsangCOMPLETED.
Bài tập:
Để đồng bộ trạng thái giao dịch từ MongoDB sang Kafka hoặc Elasticsearch phục vụ cho hệ thống đối soát, bạn sẽ dùng phương pháp Oplog Tailing thuần túy hay Change Streams? So sánh chi tiết về Performance Overhead và Developer Experience.
Nếu Node Primary mà client đang mở Change Streams bị sập, Driver của MongoDB xử lý Resume Token như thế nào để kết nối lại vào Node Primary mới mà không bị lỡ bất kỳ event nào?
Ngày 28: Internals của SCRAM, RBAC & Cấp quyền tự động hóa (CI/CD)
Kiểm soát truy cập không chỉ là cấp quyền, mà là hiểu Database lưu trữ và cache các quyền đó trong RAM như thế nào để không làm chậm luồng truy vấn.
Lý thuyết:
Kiến trúc IAM của MongoDB: Cách các collections
admin.system.usersvàadmin.system.rolesđược đồng bộ qua Oplog.Authorization Cache: Mỗi khi một kết nối (connection) được mở, MongoDB cache các quyền của user đó vào RAM. Điều gì xảy ra khi bạn thu hồi quyền (revoke) đột ngột? Khái niệm User Cache Invalidation.
Cơ chế Hash SCRAM-SHA-256: Quá trình client và server trao đổi "bằng chứng" (proof) mã hóa mà không bao giờ gửi password thật qua mạng.
User-Defined Roles (Quyền tùy chỉnh cực hạn): Phân tích khái niệm
ActionvàResource.
Thực hành:
Tạo một custom role không cho phép con người sử dụng, chỉ dành riêng cho các service accounts của pipeline tự động hóa (như ArgoCD hoặc Jenkins).
Role này CHỈ có quyền tạo Index (
createIndex) và cập nhật dữ liệu trên bảngtransactions, tuyệt đối KHÔNG có quyềndrop,deletehaygrantRole.
Bài tập:
Trình bày quy trình "Principle of Least Privilege" áp dụng cho một hệ thống gồm: 1 App Backend, 1 Tool Monitor (Grafana), và 1 tiến trình Backup chạy ngầm. Gán các quyền built-in nào là đủ và an toàn nhất?
Giải thích rủi ro bảo mật nếu sử dụng
SCRAM-SHA-1thay vìSCRAM-SHA-256đối với các cuộc tấn công brute-force từ bảng băm (rainbow tables).
Ngày 29: Single Sign-On (SSO) & Cấu trúc Token phân tán (OIDC/LDAP)
Quản lý hàng ngàn user bằng SCRAM nội bộ là thảm họa vận hành. Một Platform Engineer cần biết cách đẩy trách nhiệm xác thực cho các hệ thống định danh tập trung.
Lý thuyết:
OpenID Connect (OIDC) Internals: Cách MongoDB tiếp nhận JWT (JSON Web Token) từ Identity Provider (IdP). Luồng xác thực Machine-to-Machine.
LDAP Proxy & Authentication Pass-Through: Cách
mongostrong Sharded Cluster đóng vai trò proxy, nhận request xác thực và đẩy (forward) nó xuống các Config Servers để kiểm tra, tránh việc mỗi Shard phải tự gọi LDAP.
Thực hành:
Thiết lập kết nối giữa MongoDB và một Identity Provider (ví dụ: Google Workspace, Keycloak, hoặc Okta) sử dụng OIDC.
Decode một JWT token bằng JWT.io, phân tích các trường
iss(Issuer),aud(Audience), và mapping cácgroupstrong token thành các Roles trong MongoDB.
Bài tập:
Vẽ sơ đồ Sequence Diagram mô tả luồng một Microservice cần ghi dữ liệu vào Shard 1, xác thực qua
mongosbằng OIDC Token sinh ra từ hệ thống IAM của ngân hàng.Trong trường hợp LDAP server bị sập, MongoDB có cơ chế cache thông tin đăng nhập của LDAP user để duy trì kết nối không? Thời gian cache (TTL) mặc định là bao lâu?
Ngày 30: Mutual TLS (mTLS) & Zero-Downtime Certificate Rotation
Keyfile là cơ chế bảo mật nội bộ đã lỗi thời. Các hệ thống Enterprise bắt buộc phải dùng chứng chỉ số X.509 để các node tự xác thực lẫn nhau.
Lý thuyết:
Transport Layer Security (TLS 1.3): Quá trình TLS Handshake giữa Client và Server.
Internal Membership Authentication bằng mTLS: Node A (Primary) và Node B (Secondary) trao đổi chứng chỉ X.509. MongoDB đọc các trường Subject Alternative Name (SAN), Organization (O), và Organizational Unit (OU) để quyết định Node B có phải là "người nhà" hay không.
Certificate Revocation List (CRL) & OCSP: Cơ chế thu hồi chứng chỉ ngay lập tức khi một node bị nghi ngờ xâm nhập.
Thực hành:
Dựng một CA (Certificate Authority) nội bộ bằng OpenSSL. Tạo chứng chỉ cho 3 node trong cụm Replica Set.
Cấu hình tham số
net.tls.clusterFileép các node phải dùng X.509 để giao tiếp, loại bỏ hoàn toàn Keyfile.Thực hiện thao tác Certificate Rotation (Thay chứng chỉ mới) mà không làm gián đoạn (Zero-Downtime) hoạt động của database.
Bài tập:
Nếu chứng chỉ
clusterFilehết hạn (expired) vào lúc 12h đêm, điều gì sẽ ngay lập tức xảy ra với luồng Replication của Replica Set?Trình bày kịch bản kiến trúc hạ tầng: Làm sao để tích hợp Cert-Manager (nếu chạy trên Kubernetes) để tự động cấp phát và xoay vòng chứng chỉ TLS cho cụm MongoDB mỗi 90 ngày?
Ngày 31: Encryption at Rest & Key Management Interoperability Protocol (KMIP)
Mã hóa ổ đĩa (Disk Encryption) của hệ điều hành là chưa đủ. Dữ liệu phải được mã hóa ngay tại tầng Storage Engine trước khi chạm xuống filesystem.
Lý thuyết:
WiredTiger Encryption Internals: MongoDB không mã hóa toàn bộ file, nó mã hóa từng Block/Page trong RAM ngay trước khi xả (flush) xuống đĩa. Thuật toán AES-256-GCM.
Kiến trúc khóa 2 tầng (Two-Tier Key Architecture): * Database Key (Khóa dữ liệu nội bộ - DEK).
Master Key (Khóa chủ - CMK) dùng để bọc (wrap) DEK.
Tích hợp External KMS: Sử dụng giao thức KMIP để đẩy Master Key ra khỏi máy chủ MongoDB, lưu trữ tại các Vault chuyên dụng (AWS KMS, GCP Cloud KMS, HashiCorp Vault).
Thực hành:
Cấu hình bật tính năng
security.enableEncryptionsử dụng local key file để hiểu luồng cơ bản.(Tùy chọn mô phỏng): Phân tích file cấu hình để MongoDB gọi API sang AWS KMS, sử dụng IAM Role của máy chủ để tự động fetch Master Key khi khởi động tiến trình
mongod.
Bài tập:
So sánh chi phí hiệu năng (CPU overhead và RAM) khi bật tính năng Encryption at Rest của WiredTiger so với việc chạy bình thường.
Theo chuẩn bảo mật PCI-DSS, nếu Master Key trên hệ thống KMS bị rò rỉ, bạn phải thực hiện quy trình Key Rotation (Xoay khóa). Quá trình MongoDB thay thế Master Key mới có yêu cầu phải giải mã và mã hóa lại toàn bộ Terabytes dữ liệu trên ổ cứng không? Giải thích cơ chế wrap/unwrap.
Ngày 32: Data-In-Use Encryption (CSFLE & Queryable Encryption)
Đây là công nghệ khó nhất và "đắt tiền" nhất của MongoDB: Dữ liệu bị mã hóa ở phía Client, đi qua mạng ở dạng mã hóa, lưu trên đĩa ở dạng mã hóa, và Server có thể tìm kiếm trên dữ liệu đó mà không có chìa khóa giải mã.
Lý thuyết:
Client-Side Field Level Encryption (CSFLE): Mã hóa cục bộ 1 vài trường (ví dụ: Căn cước công dân, số thẻ tín dụng). Tuy nhiên, CSFLE chỉ hỗ trợ truy vấn so sánh bằng (Equality Match).
Queryable Encryption (QE) - Kỷ nguyên mới: Kiến trúc sinh ra các "Beacon Collections" và "Cryptographic Tokens" kèm theo. Nó cho phép thực hiện tìm kiếm bằng (Equality), tìm kiếm dải (Range - lớn hơn, nhỏ hơn), và Prefix mà bản thân RAM và CPU của MongoDB không hề biết dữ liệu gốc là gì.
Tách biệt đặc quyền (Separation of Duties): Team vận hành Cloud/Database cầm quyền root nhưng chỉ nhìn thấy chuỗi Base64 vô nghĩa. Chỉ có Application Service cầm DEK mới đọc được data.
Thực hành:
Viết một đoạn code Node.js hoặc Python sử dụng thư viện
mongodb-client-encryption.Cấu hình mã hóa tự động trường
account_balancevànational_idtrong collection giao dịch chuyển tiền.Thực thi truy vấn
find({ account_balance: { $gt: 10000 } })ngay trên dữ liệu đã mã hóa (sử dụng tính năng QE Range queries mới nhất).
Bài tập:
Phân tích sự đánh đổi cực lớn về mặt kích thước lưu trữ (Storage Bloat) khi áp dụng Queryable Encryption. Tại sao 1 trường text 10 byte sau khi mã hóa QE có thể phình to lên vài trăm byte cùng với việc sinh ra các bảng metadata đi kèm?
Trình bày thiết kế kiến trúc phân phối Data Encryption Key (DEK). Làm sao để Microservice A (xử lý chuyển tiền) và Microservice B (báo cáo thống kê) có thể cùng chia sẻ một khóa DEK một cách an toàn mà không bị hardcode trong source code?
Ngày 33: Enterprise Auditing Internals & Hardware/Network Hardening
Ghi log Audit cho hệ thống ngân hàng là bắt buộc, nhưng nếu ghi log mọi thao tác CRUD, Disk I/O của bạn sẽ quá tải ngay lập tức (hiệu ứng Observer Effect).
Lý thuyết:
Kiến trúc Auditing của MongoDB Enterprise: Luồng sự kiện đi qua Audit daemon. Sự khác biệt về chi phí I/O giữa việc ghi log ra định dạng BSON, JSON, và Syslog.
Audit Filter (Cú pháp BSON cấp thấp): Cách viết filter để MongoDB chỉ capture lại những sự kiện nhạy cảm (ví dụ: Ai đó drop database, hoặc một user không thuộc group
app-serverscố gắng query bảng của project).Network Hardening (K8s/Cilium Integration): Bảo vệ MongoDB không chỉ bằng Firewall/iptables. Tích hợp CNI như Cilium để chặn truy cập ở tầng eBPF (Layer 4/Layer 7), đảm bảo chỉ có Pods mang đúng identity mới được kết nối tới cổng 27017.
Thực hành:
Kích hoạt
auditLogvà cấu hình bộ lọc (filter) để CHỈ ghi log khi có thao tácupdatehoặcdeletethành công trên collection giao dịch, bỏ qua mọi thao tácfind.Phân tích một dòng log Audit BSON (trích xuất các trường
atype,ts,local,remote,users,roles).
Bài tập:
Đánh giá rủi ro: Nếu ổ đĩa chứa Audit Log bị đầy (100% Disk Space), tiến trình
mongodsẽ xử lý như thế nào? (Gợi ý: Tìm hiểu cơ chếauditLog.failIndexKeyTooLonghoặc hành vi crash chủ động để bảo vệ compliance).Một DBA có quyền
rootcó thể tự ý tắt hệ thống Audit để xóa dấu vết không? Đề xuất kiến trúc Role-Based Access Control kết hợp với Log Forwarding (gửi log ra ngoài cụm) để chống lại kịch bản "người trong nhà" này.
Ngày 34: Data Modeling Core Physics (BSON, RAM & Working Set)
Trước khi vẽ ERD hay class diagram, bạn phải tính toán được một document chiếm bao nhiêu byte trên đĩa và bao nhiêu KB trong RAM.
Lý thuyết:
Working Set Size (WSS): Nguyên tắc sinh tử của NoSQL: Toàn bộ dữ liệu thường xuyên truy cập (Data) + Toàn bộ Index phải nằm gọn trong RAM.
Disk I/O vs. CPU tradeoff: Nhúng dữ liệu (Embedding) giảm số lần gọi đĩa (Disk Seeks) nhưng làm tăng chi phí RAM và băng thông mạng nếu chỉ cần đọc một trường nhỏ. Tham chiếu (Referencing) tiết kiệm băng thông nhưng gây ra Random I/O trên đĩa cứng.
Unbounded Arrays (Mảng không giới hạn): Sát thủ vô hình của MongoDB. Tại sao một mảng liên tục to ra (ví dụ: mảng
historytrong document user) sẽ làm thay đổi vị trí của document trên đĩa, gây phân mảnh (fragmentation) và tăng vọt chi phí Oplog?
Thực hành:
Tạo một document khách hàng (user) với mảng
logsnhúng bên trong.Viết một vòng lặp update
\(push5.000 phần tử vào mảng này. Đo đạc execution time và theo dõi sự phình to củaoplog.rstương ứng với mỗi thao tác\)push.
Bài tập:
Trình bày công thức toán học để ước tính dung lượng RAM cần thiết (Capacity Planning) cho một cụm MongoDB chứa 100 triệu bản ghi, biết mỗi bản ghi nặng trung bình 2KB và có 3 Indexes (kích thước trung bình 30 byte/key).
Nếu nhúng (embed) toàn bộ lịch sử trạng thái của một giao dịch (
PENDING->AML_CHECK->CLEARED) vào cùng một document thay vì tách bảng, Oplog sẽ phải gánh chịu chi phí khuếch đại ghi (Write Amplification) như thế nào?
Ngày 35: Advanced Schema Patterns - Bucket & Polymorphic
Dữ liệu trong thế giới thực không hề đồng nhất. Một kiến trúc sư phải biết bẻ cong luật lệ để ép dữ liệu phục vụ tốt nhất cho Query.
Lý thuyết:
The Polymorphic Pattern (Đa hình): Khi bạn có nhiều loại giao dịch (Swift, Ripple, Internal Transfer). Thay vì tạo 3 collections khác nhau và phải JOIN khi làm báo cáo tổng, hãy gộp vào một collection duy nhất và dùng trường phân biệt (Discriminator field) như
type.The Bucket Pattern: Cách tuyệt hảo nhất để lưu trữ chuỗi thời gian (Time-series) hoặc dữ liệu sinh ra liên tục. Gom nhóm (bucket) các giao dịch theo từng ngày hoặc tháng vào một document với mảng nội bộ để giảm thiểu số lượng Index entry và tối ưu Disk I/O.
The Attribute Pattern: Khi một document có các trường thay đổi động (ví dụ: metadata cấu hình của từng ngân hàng đối tác), làm sao để đánh Index mà không dính giới hạn số lượng Index của MongoDB? (Chuyển Key-Value thành mảng các Object).
Thực hành:
Xây dựng mô hình Bucket Pattern cho log đối soát cuối ngày: Một document đại diện cho 1 tài khoản trong 1 ngày, chứa thông tin
start_balance,end_balance, và mảngtransactionschứa các giao dịch trong ngày đó.Đánh giá dung lượng Index khi so sánh cách dùng Bucket Pattern với cách lưu từng giao dịch thành một document riêng biệt.
Bài tập:
Bạn đang chuẩn bị slide để thuyết trình tại cộng đồng kỹ thuật. Hãy phác thảo sơ đồ so sánh (Trade-off) giữa Polymorphic Pattern và mô hình Multi-Tables truyền thống trong SQL khi áp dụng cho mảng thanh toán quốc tế.
Điểm yếu lớn nhất của Bucket Pattern khi cần truy vấn và cập nhật một phần tử ngẫu nhiên nằm sâu bên trong mảng của Bucket là gì?
Ngày 36: Handling Extreme Outliers & Working Set Optimization
Hệ thống hiếm khi sập vì khách hàng bình thường. Nó sập vì các "Outliers" – những trường hợp ngoại lai có lượng dữ liệu khổng lồ phá vỡ mọi quy luật.
Lý thuyết:
The Outlier Pattern: 99% người dùng có dưới 100 giao dịch/năm (phù hợp để nhúng mảng). Nhưng 1% khách hàng hạng siêu VIP hoặc tài khoản của đối tác (Merchant) có tới 50.000 giao dịch/ngày. Làm sao thiết kế Schema phục vụ chung cả 2 tệp mà không bị dính giới hạn 16MB document?
The Subset Pattern (Tối ưu Working Set): Tách một document khổng lồ thành 2 phần: Dữ liệu "nóng" (hiển thị ngay trên màn hình app) giữ ở collection chính, dữ liệu "lạnh" (ít khi dùng tới) đẩy sang collection khác.
The Computed Pattern: Trade-off giữa CPU và Disk I/O. Thay vì tính tổng doanh thu bằng Aggregation mỗi lần load trang, hệ thống tự động cộng dồn số dư vào một trường có sẵn ngay lúc ghi dữ liệu (Read-Heavy vs Write-Heavy).
Thực hành:
Áp dụng Outlier Pattern: Thêm cờ (flag)
has_extra_transactions: truevào document của khách hàng VIP.Viết một Application logic (giả mã Node/Python): Nếu đọc thấy cờ này là
false, trả về mảng nội bộ. Nếu làtrue, gọi thêm một query thứ 2 xuống collectiontransactions_overflowđể lấy toàn bộ dữ liệu.
Bài tập:
Một tài khoản tổng (Nostro/Vostro) của ngân hàng liên tục nhận và chuyển tiền, dẫn đến thao tác cộng trừ số dư (Computed Pattern) bị nghẽn (Lock contention). Áp dụng kỹ thuật chia nhỏ bản ghi (Document Versioning/Accounting pattern) để giải quyết nghẽn cổ chai này.
Ưu và nhược điểm của Subset Pattern khi hệ thống bất ngờ nhận yêu cầu xuất báo cáo (Export Excel) cần toàn bộ dữ liệu cả Nóng và Lạnh?
Ngày 37: Schema Evolution (Zero-Downtime) & Idempotency
Database không bao giờ đứng im. Thử thách của Architect là thay đổi cấu trúc bảng, migrate dữ liệu của hàng triệu khách hàng mà không được phép có downtime.
Lý thuyết:
Schema Versioning Pattern: Không dùng lệnh
updateManyđể sửa toàn bộ DB trong 1 đêm (gây gián đoạn dịch vụ). Gắn trườngschema_versionvào mỗi document. Tầng code Application sẽ xử lý cả 2 versions trong giai đoạn chuyển giao.Tính lũy đẳng dữ liệu (Idempotency) ở tầng Schema: Cơ chế phòng thủ khi Message Queue (RabbitMQ/Kafka) gửi đúp sự kiện (At-least-once delivery).
Sử dụng Unique Index kết hợp Hashing: Tạo một chuỗi hash từ nội dung giao dịch để làm
_idhoặc đánh Unique Index, ép MongoDB tự động báo lỗiDuplicateKeyExceptionvà bỏ qua giao dịch trùng.
Thực hành:
Mô phỏng chuyển đổi Schema từ V1 (lưu tên đầy đủ trong 1 string) sang V2 (tách ra
first_namevàlast_name).Viết một Middleware ở tầng Code xử lý logic: Khi đọc lên, nếu là V1 thì on-the-fly (tức thời) map sang cấu trúc V2 để trả về API, đồng thời spawn một tiến trình bất đồng bộ (async) ghi đè lại document thành V2 xuống Database.
Bài tập:
Trình bày quy trình 4 bước để Migrate Schema không downtime: (1) App hỗ trợ cả 2 schema, (2) App ghi theo schema mới nhưng đọc cả 2, (3) Script ngầm quét đổi data cũ, (4) Cập nhật App bỏ support schema cũ. Ước lượng rủi ro của từng bước.
Áp dụng Transaction của MongoDB có giải quyết được bài toán xử lý trùng lặp thông điệp (Duplicate Event Processing) từ một hệ thống bên ngoài gọi vào không? Phân tích tại sao tầng Unique Index ở Database vẫn là chốt chặn cuối cùng bắt buộc phải có.
Ngày 38: Internals của JSON Schema & Data Governance cấp thấp
Xác thực dữ liệu ở tầng Application (như dùng Mongoose) là không đủ an toàn, vì bất kỳ ai có quyền truy cập DB đều có thể chèn dữ liệu bẩn. Validation phải được khóa chặt ở tầng Database.
Lý thuyết:
Kiến trúc Schema Validation: Cách MongoDB engine parse
$jsonSchemaBSON object. Hiệu năng bị suy giảm (write penalty) ra sao khi bật validation phức tạp?validationLevel&validationAction: Cơ chếstrictvsmoderate. Sự khác biệt cực kỳ quan trọng củamoderate(chỉ chặn update sai trên các document đã hợp lệ từ trước, bỏ qua các document lỗi từ hệ thống cũ - legacy data).Validation Error Logging: Khi dùng
validationAction: "warn", làm sao để theo dõi các lỗi này qua logs củamongodđể làm báo cáo Data Quality mà không chặn luồng ghi thực tế?
Thực hành:
Viết một
$jsonSchemacực kỳ nghiêm ngặt cho collectiontransactions: Bắt buộc trườngamountlà kiểudecimalvàminimum: 0(chống số âm), trườngstatusphải nằm trongenum: ["PENDING", "CLEARED", "REJECTED"].Sử dụng lệnh
db.runCommand({ collMod: "transactions", validator: { ... } })để áp dụng.
Bài tập:
Đánh giá rủi ro khóa (Locking): Khi bạn chạy lệnh
collModđể thêm Schema Validation vào một collection đang có 100 triệu bản ghi trên Production, lệnh này yêu cầu loại Lock nào (Exclusive Lock hay Intent Lock)? Có gây downtime không?Cố tình chèn một bản ghi lỗi để kích hoạt
validationAction: "warn". Truy cập filemongod.logvà trích xuất BSON ID của bản ghi gây ra cảnh báo.
Ngày 39: Optimistic Concurrency Control (OCC) & The State Machine Pattern
Thay vì dùng Distributed Transactions gây nghẽn cổ chai (Lock Contention), Architect giỏi sẽ xử lý tính nhất quán (Consistency) bằng các pattern không cần khóa (Lock-free).
Lý thuyết:
Optimistic Concurrency Control (OCC): Kiểm soát đồng thời lạc quan. Sử dụng một trường
version(hoặclast_modified) để đảm bảo không có 2 tiến trình cùng cập nhật một document tại cùng một phần nghìn giây.The State Machine Pattern: Mô hình hóa luồng chuyển tiền bằng trạng thái. Document chỉ được phép chuyển từ trạng thái A sang B nếu nó đang thực sự ở A. Khái niệm Compare-and-Swap (CAS) trong NoSQL.
Thực hành:
Chèn một giao dịch với cấu trúc:
{ _id: 1, amount: 100, status: "PENDING", version: 1 }.Viết câu lệnh update kết hợp OCC:
db.transactions.updateOne({ _id: 1, status: "PENDING", version: 1 }, { \(set: { status: "CLEARED" }, \)inc: { version: 1 } }).
Bài tập:
Kịch bản Race Condition: Có 2 Microservices cùng lúc nhận được lệnh "Duyệt giao dịch" cho bản ghi số 1. Cả 2 cùng đọc lên thấy
version: 1và cùng gửi lệnhupdateOne. Phân tích ở tầng WiredTiger, làm sao OCC đảm bảo tiến trình đến chậm hơn 1 microsecond sẽ nhận về kết quảmatchedCount: 0vàmodifiedCount: 0?Ở tầng Code, khi Microservice nhận được
modifiedCount: 0, nó phải hành xử như thế nào (Retry, Báo lỗi, hay Ignore)?
Ngày 40: The Extended Reference Pattern & Bùng nổ 1-n
Khi thiết kế một bảng chứa Giao dịch (hàng trăm triệu) trỏ về bảng Khách hàng, dùng tham chiếu (Reference) sẽ ép hệ thống phải JOIN liên tục. Nhúng (Embedding) thì phá vỡ giới hạn 16MB.
Lý thuyết:
The Extended Reference Pattern: Kỹ thuật nhúng một phần (Partial Embedding). Chỉ copy những trường dữ liệu ít thay đổi và được truy vấn nhiều nhất (ví dụ:
customer_name,kyc_level) từ bảng Customer sang bảng Transaction. Các chi tiết khác vẫn để ở bảng Customer.Data Duplication & Consistency: Đánh đổi (Trade-off) giữa tốc độ Đọc (O(1)) và tốc độ Ghi.
Unbounded Arrays (Anti-pattern): Tại sao việc thiết kế mảng
transactions: []bên trong documentcustomerlà cách nhanh nhất để làm sập Database khi khách hàng đó là một doanh nghiệp có hàng nghìn giao dịch mỗi ngày.
Thực hành:
Xây dựng collection
customersvà collectiontransactions.Trong collection
transactions, áp dụng Extended Reference bằng cách lưu cảcustomer_id,customer_namevàcustomer_tier.
Bài tập:
Khi khách hàng cập nhật
customer_nameở bảngcustomers, dữ liệu ở hàng triệu bản ghitransactionslịch sử của họ bị sai lệch (Stale Data). Với đặc thù của ngành tài chính, bạn sẽ chạy một job ngầm để update lại toàn bộ lịch sử, HAY giữ nguyên tên cũ vì lý do Audit/Immutable ledger? Giải thích lập luận thiết kế.Trình bày điểm giới hạn (Threshold) về mặt toán học. Khi nào một mối quan hệ 1-N buộc phải chuyển từ Embedding sang Extended Reference (Dựa vào số lượng phần tử N và Working Set Size)?
Ngày 41: Mô hình hóa cấu trúc Cây/Đồ thị & Anti-Money Laundering (AML)
Trong ngành ngân hàng, việc truy vết dòng tiền (ví dụ: A chuyển cho B, B chuyển cho C, C chuyển lại cho A) là bài toán Graph Database, nhưng MongoDB hoàn toàn làm được nếu mô hình hóa đúng.
Lý thuyết:
Materialized Paths Pattern: Lưu trữ đường dẫn phân cấp thành một chuỗi string (Ví dụ:
path: "/A/B/C"). Cực kỳ mạnh mẽ để query bằng Regex.Array of Ancestors Pattern: Lưu trữ toàn bộ tổ tiên của một node vào một mảng (Ví dụ:
ancestors: ["A", "B", "C"]). Cực kỳ tối ưu cho Index B-Tree (Multikey Index).$graphLookupInternals: Cách Aggregation Framework thực thi truy vấn đệ quy (recursive search). Các tùy chọnmaxDepthvàrestrictSearchWithMatchđể tránh vắt kiệt RAM.
Thực hành:
Chèn một tập dữ liệu mô phỏng dòng tiền:
A -> B -> C -> D.Viết một pipeline sử dụng
$graphLookupbắt đầu từ A, kết nốito_accountvớifrom_accountđể tìm tất cả các tài khoản nhận tiền gián tiếp từ A trong tối đa 3 bước (Depth = 3).
Bài tập:
Thiết kế truy vấn phát hiện "Circular Loop" (Vòng lặp rửa tiền). Làm sao để
$graphLookupchỉ ra được dòng tiền đã quay ngược lại tài khoản gốc?So sánh hiệu năng và chi phí Index giữa Materialized Paths và Array of Ancestors khi bạn cần tìm "Tất cả các giao dịch là con cháu của tài khoản Root_A".
Ngày 42: Schema Refactoring ở quy mô cực đại & Data Migration
Lý thuyết:
The Schema Versioning Pattern: Xử lý sự không đồng nhất. Gắn trường
schema_version: 2vào các tài liệu định dạng mới. Tầng Application (Repository pattern) chịu trách nhiệm nhận diện version và ánh xạ (map) ra Object thống nhất.Background Migration: Đừng bao giờ dùng
updateManykhông cósleep()để migrate hàng triệu bản ghi. Quá trình này sẽ sinh ra Oplog ồ ạt, đánh sập các Node Secondary do Replication Lag, và vắt kiệt Disk IOPS.The Outbox Pattern (Dual Write): Ghi dữ liệu đồng thời vào cả Schema Cũ và Schema Mới trong quá trình chuyển giao thông qua một công cụ Change Data Capture (CDC) như Debezium hoặc Change Streams.
Thực hành:
Viết một đoạn script (Node.js/Python) thực hiện Migration Data Background. Script phải dùng
.find().limit(1000)để batching, thực hiệnbulkWrite, sau đó chủ độngsleep(100ms)trước khi chạy batch tiếp theo để nhường IOPS cho ứng dụng.Bài tập:
Một hệ thống core cũ đang đẩy dữ liệu vào MongoDB. Bạn phát hiện có 100,000 document đang bị cấu trúc sai do lỗi code ngày hôm qua. Trình bày một Standard Operating Procedure (SOP) để sửa 100,000 document này trên môi trường Production mà không gây ra bất kỳ ảnh hưởng nào tới API đang hoạt động.
Khi áp dụng Schema Versioning, Query logic của bạn bắt buộc phải quét cả cấu trúc cũ và mới. Trình bày cách xây dựng Index sao cho nó bao phủ (Covered Query) được cả 2 version của Schema mà không gây hiện tượng Collection Scan.
Ngày 43: Data Modeling cho cấu trúc phân cấp (Hierarchies) & Cây phân nhánh
Trong hệ thống banking, bạn thường xuyên phải xử lý cây phân cấp: Quốc gia -> Vùng -> Chi nhánh cấp 1 -> Chi nhánh cấp 2, hoặc cấu trúc cây của sổ cái kế toán (Chart of Accounts).
Lý thuyết:
Materialized Paths: Lưu trữ toàn bộ đường dẫn phân cấp thành chuỗi (ví dụ:
path: ",GLOBAL,VN,HANOI,BRANCH_01,"). Sức mạnh của Regex^,GLOBAL,VN,kết hợp với Index để tìm kiếm toàn bộ chi nhánh con trong O(1).The Closure Table Pattern (Bảng bao đóng): Sử dụng một collection phụ để lưu TRỰC TIẾP mọi mối quan hệ tổ tiên - con cháu ở mọi cấp độ. Cực kỳ tốn dung lượng nhưng tốc độ truy vấn cây sâu (Deep Tree) là vô địch, không cần đệ quy.
Tree Updates (Cập nhật cây): Nỗi ám ảnh khi một Chi nhánh mẹ đổi tên hoặc chuyển vùng. Phân tích chi phí Write Amplification khi sử dụng từng Pattern.
Thực hành:
Xây dựng collection
branchessử dụng Materialized Paths.Tạo Index trên trường
path. Chạy truy vấn.find({ path: /^,GLOBAL,VN,/ })và dùng.explain()để xác nhận MongoDB thực sự dùng Index (Index Bounds) chứ không phải quét toàn bảng bằng Regex.
Bài tập:
So sánh chi phí duy trì (Maintenance Cost) khi một node mẹ chuyển sang nhánh khác giữa Materialized Path và Nested Sets (Tập hợp lồng nhau).
Viết một Aggregation Pipeline kết hợp
$graphLookupđể tái tạo lại toàn bộ cây phân cấp từ mô hình Parent References (chỉ lưuparent_id) thành một object JSON có cấu trúc lồng nhau (nested children).
Ngày 44: Linux OS Tuning (Phần 1): NUMA Architecture & Memory Management (THP)
WiredTiger quản lý bộ nhớ rất tốt, nhưng nếu hệ điều hành Linux "phá bĩnh", database vẫn sẽ treo (stall).
Lý thuyết:
NUMA (Non-Uniform Memory Access): Kiến trúc CPU hiện đại chia RAM thành các node gắn với từng Socket CPU. Tại sao MongoDB trên Linux bắt buộc phải chạy với chính sách
numactl --interleave=all? Hiện tượng "Swap Insanity" khi một NUMA node bị đầy dù tổng RAM vẫn còn.Transparent Huge Pages (THP): Tính năng mặc định của Linux gộp 512 trang RAM nhỏ (4KB) thành 1 trang khổng lồ (2MB). Tại sao THP lại là "kẻ thù số 1" của các database có mật độ truy cập ngẫu nhiên (Random I/O) cao như MongoDB? Quá trình khép trang (page defragmentation) gây lock CPU ra sao?
OOM Killer (Out Of Memory Killer): Thuật toán
badness()của Linux. Cách bảo vệ tiến trìnhmongodkhông bị hệ điều hành "giết" khi server cạn RAM bằngoom_score_adj.
Thực hành:
Kiểm tra trạng thái THP trên server Linux (hoặc container):
cat /sys/kernel/mm/transparent_hugepage/enabled.Viết một systemd service script để vô hiệu hóa hoàn toàn THP (cả
enabledvàdefrag) trước khi tiến trìnhmongodkhởi động.
Bài tập:
Trình bày hiện tượng: Server có 128GB RAM. MongoDB được set Cache 60GB. Tuy nhiên, lệnh
topcho thấy RAM bị dùng 99% và hệ thống bắt đầu dùng Swap, làm Database chậm như rùa. Giải thích vai trò của OS Filesystem Cache và tại sao việc này là bình thường (nhưng dùng Swap thì bất thường).Tham số
swappinesstrong sysctl (vm.swappiness) quyết định điều gì? Khuyến nghị giá trị này là 1 hay 0 đối với server chạy database lõi?
Ngày 45: Linux OS Tuning (Phần 2): Filesystem (XFS), Readahead & ulimit
Nếu ổ cứng là cái kho, thì Filesystem là cách bạn sắp xếp kho. Xếp sai cách, I/O của WiredTiger sẽ nghẽn ở cổ chai của Kernel.
Lý thuyết:
XFS vs Ext4: Tại sao tài liệu chính thức của MongoDB khuyến cáo mạnh mẽ dùng XFS? Khả năng cấp phát khối song song (parallel allocation) của XFS giúp giải quyết vấn đề I/O lock trong quá trình WiredTiger checkpoint như thế nào.
Block Device Readahead: Cơ chế Linux tự động đọc trước dữ liệu vào RAM khi đoán bạn sẽ cần. Tại sao Readahead lớn (ví dụ 256KB) lại cực tốt cho Streaming (như xem phim), nhưng lại "giết chết" RAM của MongoDB khi query ngẫu nhiên?
Tài nguyên tiến trình (
ulimit): Nắm vữngnofile(File Descriptors) vànproc(Số lượng tiến trình/threads). Mỗi một connection từ Client đến DB tiêu tốn 1 File Descriptor và 1 Thread.
Thực hành:
Sử dụng lệnh
blockdev --getrađể kiểm tra thông số readahead của ổ đĩa. Thay đổi nó về mức 32 (16KB) hoặc 0 tùy theo kiến trúc NVMe hay SSD.Cấu hình file
/etc/security/limits.confđể cấp cho usermongodgiới hạn 64000nofilevà 64000nproc.
Bài tập:
Khi log của MongoDB báo lỗi
Too many open fileshoặcSocketException: 24, nguyên nhân sâu xa ở tầng OS là gì? Cách trace xem tiến trìnhmongodđang giữ bao nhiêu file/socket đang mở?Giải thích cơ chế "Thread per Connection" của MongoDB. Nếu ứng dụng có 20,000 users đang online cùng lúc và mở thẳng 20,000 kết nối TCP đến Database, CPU của server database sẽ bị vắt kiệt bởi hành động gì (Context Switching)?
Ngày 46: Driver Internals & Cấu hình Connection Pool (Chống bão Traffic)
Connection Pool không sinh ra để "làm nhanh hơn", nó sinh ra để "bảo vệ Database không bị sập".
Lý thuyết:
Kiến trúc Connection Pool: Hiểu các trạng thái:
Idle,In Use,Pending.maxPoolSize& Khủng hoảng Context Switching: Tại sao setmaxPoolSize= 1000 lại khiến DB chậm hơn rất nhiều so vớimaxPoolSize= 100? Quy tắc Little's Law và công thức ước tính Pool Size dựa trên số lượng Core CPU thực tế của Database.Wait Queue & Timers: Khi Pool hết kết nối, request bị đưa vào Hàng đợi (Wait Queue). Các tham số sinh tử:
waitQueueTimeoutMS(bị văng lỗi sau bao lâu chờ đợi) vàmaxIdleTimeMS(khi nào thì đóng kết nối dư thừa).Connection Storms (Bão kết nối): Xảy ra khi hệ thống Microservices (K8s) scale up đột ngột 50 pods, mỗi pod cố gắng mở hàng trăm kết nối cùng lúc, đánh sập Thread Pool của
mongod.
Thực hành:
Viết một ứng dụng Node.js/Python cấu hình
maxPoolSize = 5.Sử dụng Promise.all (hoặc Asyncio) bắn đồng thời 50 query tốn thời gian (ví dụ sleep 2 giây).
Bật log debug ở Driver để quan sát 5 kết nối được xoay vòng ra sao, và các truy vấn còn lại phải nằm chờ ở Wait Queue như thế nào.
Bài tập:
Giả sử bạn có 10 Microservices. Mỗi service chạy 3 Pods. Mỗi Pod set
maxPoolSize = 50. Tổng số kết nối tối đa dội vào MongoDB là bao nhiêu? Nếu CPU của DB chỉ có 8 Cores, thiết kế này có hợp lý không?Cấu hình TCP Keepalive (
net.ipv4.tcp_keepalive_time) trên Linux liên quan mật thiết thế nào đến lỗi "Connection reset by peer" do Firewall hoặc Load Balancer tự động ngắt các kết nối nhàn rỗi (idle) của MongoDB Driver?
Ngày 47: Memory Allocator (tcmalloc) & Linux Profiling (perf, strace)
Khi bạn đã tuning mọi thứ mà hệ thống vẫn chậm một cách bí ẩn, bạn phải soi xuống tầng C++ và Kernel.
Lý thuyết:
tcmalloc: MongoDB không dùng cấp phát bộ nhớ chuẩn của C (glibcmalloc) mà dùng Thread-Caching Malloc (tcmalloc). Hiểu cách nó chia RAM thành các "Central Free List" và "Thread Cache" để tránh lock khi cấp phát.Memory Fragmentation (Phân mảnh bộ nhớ): Tại sao Database dùng 80GB RAM, nhưng thực tế dữ liệu chỉ có 50GB? Số RAM chênh lệch bị mắc kẹt (fragmentation) trong các Free List của
tcmallocdo cấp phát/giải phóng liên tục.Linux Kernel Profiling: Sử dụng
straceđể bắt các system calls (lời gọi hệ thống) của tiến trìnhmongod(ví dụ: xem nó đang kẹt ở I/O đĩapread64hay kẹt ở mạngepoll_wait). Sử dụngperfđể phân tích CPU Flamegraph.
Thực hành:
Chạy lệnh
db.serverStatus().tcmalloc. Đọc và phân tích các chỉ sốpageheap_free_bytesvàpageheap_unmapped_bytes.(Trên môi trường Test Linux): Tìm PID của tiến trình
mongod. Chạy lệnhstrace -p <PID> -ctrong 10 giây để xem thống kê các system calls tốn thời gian nhất.
Bài tập:
Trong log, bạn thấy MongoDB tự động gọi lệnh
tcmallocReleaseRate. Tham số này đóng vai trò gì trong việc éptcmallocphải trả lại bộ nhớ vật lý rảnh rỗi (unmapped memory) cho hệ điều hành?Trình bày kịch bản dùng công cụ
perf recordvà vẽ Flamegraph để tìm ra một hàm nội bộ C++ của MongoDB đang ngốn quá nhiều CPU (ví dụ phát hiện do hàm Regex quá nặng hoặc do hàm parse BSON).
Ngày 48: Nâng cấp Rolling Upgrade & Feature Compatibility Version (FCV)
Trong hệ thống 24/7, bạn không bao giờ được phép tắt toàn bộ cụm để nâng cấp. Mọi thứ phải diễn ra theo cơ chế Rolling (xoay vòng) và phải hiểu rõ FCV để phòng hờ kịch bản Rollback.
Lý thuyết:
Feature Compatibility Version (FCV): Tại sao nâng cấp file binary (chạy file thực thi bản mới) không đồng nghĩa với việc data đã ở bản mới? FCV khóa các tính năng mới của MongoDB để đảm bảo Node cũ vẫn có thể đọc hiểu Oplog của Node mới trong quá trình nâng cấp dở dang.
Quy trình Rolling Upgrade: (1) Nâng cấp từng Secondary -> (2) Step-down Primary cũ -> (3) Nâng cấp Primary (lúc này đã thành Secondary) -> (4) Chạy lệnh nâng cấp FCV.
Runtime Configuration (
setParameter): Thay đổi tham số hệ thống ngay trên RAM (ví dụ:logLevel,maxIndexBuildMemoryUsageMegabytes) mà không cần restart tiến trình, giúp phản ứng nhanh với sự cố Production.
Thực hành:
Dựng cụm Replica Set MongoDB bản 6.0 trên Docker cục bộ (nếu dùng chip kiến trúc ARM64, nhớ pull đúng image).
Thực hiện quy trình Rolling Upgrade lên bản 7.0 mà không làm đứt đoạn một script Python đang liên tục insert dữ liệu.
Bài tập:
Trong kịch bản nâng cấp gặp sự cố, bạn phát hiện ứng dụng không tương thích với bản 7.0. Hãy trình bày quy trình (SOP) để Downgrade (hạ cấp) về lại 6.0. Tại sao việc chưa nâng cấp FCV là yếu tố sống còn để có thể downgrade thành công?
Chạy lệnh
db.adminCommand({ setParameter: 1, internalQueryExecMaxBlockingSortBytes: 67108864 })để tăng RAM cho các phép sort bị block. Đánh giá rủi ro OOM (Out of memory) khi thay đổi nóng tham số này.
Ngày 49: OS Signals & Phẫu thuật tiến trình killOp
Hủy một câu lệnh đang chạy không đơn giản là ngắt kết nối từ Client. Nếu ngắt sai cách, WiredTiger có thể mất thời gian rất dài để dọn dẹp các Index hoặc Transaction đang dở dang.
Lý thuyết:
Linux Signals: Tiến trình
mongodxử lýSIGTERM(mềm) vàSIGKILL(cứng - kill -9) như thế nào? Tại saoSIGKILLlại bắt buộc WiredTiger phải chạy chế độ phục hồi (Recovery) vào lần khởi động tiếp theo, gây downtime lâu hơn?Internals của
db.killOp(): Khi bạn gửi lệnh kill một query, MongoDB không dừng query đó ngay lập tức. Nó bật một cờ (flag) ngắt. Query engine phải đi tới "Yield Point" (điểm nhường quyền) tiếp theo thì mới kiểm tra cờ này và tự sát.Chủ động phòng vệ với
maxTimeMS: Đẩy trách nhiệm giới hạn thời gian thực thi lên tầng Driver thay vì để DBA phải đi canh server và kill tay.
Thực hành:
Tạo một câu lệnh Aggregation tốn cực nhiều CPU (ví dụ: cross-join hàng triệu bản ghi không index).
Dùng
db.currentOp()tìmopidcủa tiến trình và dùngdb.killOp()để tiêu diệt nó.
Bài tập:
Khi bạn
killOpmột thao tác đang tạo Index dạng Foreground (tạo trực tiếp, block ghi), điều gì sẽ xảy ra với các cấu trúc B-Tree đang được xây dựng dở dang trên đĩa cứng?Trong các dự án, giao dịch có thể bị treo do API bên thứ 3 phản hồi chậm. Việc sử dụng
maxTimeMSở tầng code kết nối có giúp ngắt một Multi-Document Transaction đang chờ không?
Ngày 50: Workload Isolation & Data Center Awareness
Một câu lệnh báo cáo BI (Business Intelligence) chạy mất 5 phút tuyệt đối không được phép làm nghẽn CPU của luồng xử lý giao dịch.
Lý thuyết:
Phân tách luồng xử lý (Workload Isolation): Dành riêng các Node Secondary cấu hình
priority: 0vàhidden: truechỉ để phục vụ báo cáo hoặc backup. Node này không bao giờ được bầu làm Primary, không nhận request từ mảng ứng dụng chính.Read Preference Tags: Gắn tag cho node (ví dụ:
nodeType: "analytics").Bảo mật truy cập quản trị: Kết hợp chặt chẽ với IAM và Network Policy. Các hệ thống như Grafana, ArgoCD, hoặc Jenkins pipeline chỉ được phép chọc vào node mang tag
analyticshoặcops, bị cấm truy cập hoàn toàn vào Node Primary để tránh tác động hiệu năng.
Thực hành:
Cấu hình 1 cụm 4 nodes: 1 Primary, 2 Secondary (phục vụ giao dịch), và 1 Secondary cấu hình ẩn (Hidden Node) với tag
{ usage: "reporting" }.Viết chuỗi URI kết nối (Connection String) ép buộc ứng dụng báo cáo chỉ được đọc từ node có tag
reporting.
Bài tập:
Nếu Node
reportingbị trễ đồng bộ (Replication Lag) lên tới 30 phút do chạy query quá nặng, điều này có ảnh hưởng đến tốc độ ghi (Write Latency) của Primary không? Phân tích dựa trên khái niệm Cache áp lực và Oplog.Trình bày thiết kế Network Firewall để đảm bảo Jenkins Pipeline khi chạy script lấy dữ liệu Audit chỉ có thể giao tiếp qua IP của Node
reporting.
Ngày 51: Vật lý của Backup: Filesystem Snapshots vs Logical Dumps
Quên mongodump đi nếu database của bạn lớn hơn 500GB. Ở quy mô lớn, backup phải thực hiện ở tầng khối đĩa (Block-level) của hệ điều hành.
Lý thuyết:
Logical Backup (
mongodump): Cách nó quét toàn bộ B-Tree để kéo dữ liệu lên RAM, chuyển thành BSON, rồi đẩy ra mạng/đĩa. Hậu quả tàn khốc: Quét bay toàn bộ Working Set (dữ liệu đang cache nóng) khỏi RAM, làm hệ thống chậm đột biến.Physical Backup (Filesystem Snapshots): Sử dụng LVM (Linux Volume Manager) hoặc AWS EBS Snapshots để copy dữ liệu ở tầng byte đĩa. Tốc độ gần như tức thời (O(1) time), không làm nhiễu RAM của database.
Tính nhất quán (Consistency) khi Snapshot: Làm sao để snapshot đĩa cứng mà không bị lỗi file (corrupt) do WiredTiger vẫn đang ghi dữ liệu xuống? Cơ chế Journaling kết hợp lệnh
db.fsyncLock()(khóa ghi cục bộ).
Thực hành:
Sử dụng
mongodumpvới cờ--oplog. Phân tích tại sao cờ này bắt buộc phải có để đảm bảo tính nhất quán dữ liệu cho bản backup Logical.Dùng
db.fsyncLock()để khóa node, mô phỏng quá trình chụp Snapshot đĩa cứng, sau đó dùngdb.fsyncUnlock()để mở khóa.
Bài tập:
Hệ thống của bạn là Sharded Cluster với 5 Shards. Yêu cầu đặt ra là phải Backup toàn bộ Cluster sao cho dữ liệu ở Shard 1 và Shard 5 khớp nhau chính xác đến từng mili-giây (ví dụ: giao dịch chuyển từ Shard 1 sang Shard 5 phải nhất quán). Phân tích sự khó khăn của kịch bản này và cách dừng Balancer trước khi Backup.
So sánh Recovery Time Objective (RTO - Thời gian phục hồi) giữa một file
mongodump1TB và một Snapshot EBS 1TB.
Ngày 52: Disaster Recovery (DR) & Point-in-Time Recovery (PITR)
Bản backup chụp lúc 2h sáng. Lúc 10h sáng, một junior dev lỡ tay xóa toàn bộ bảng dữ liệu. Làm sao để khôi phục lại dữ liệu chính xác đến thời điểm 09:59:59?
Lý thuyết:
Point-in-Time Recovery (PITR): Sự kết hợp giữa Snapshot định kỳ và việc sao lưu liên tục (Continuous Backup) các file Oplog.
Oplog Slicing: Cắt các file Oplog theo từng khung giờ và lưu trữ riêng biệt (ví dụ đẩy lên S3 Object Storage).
Oplog Replay: Khôi phục Snapshot từ 2h sáng, khởi động database ở chế độ standalone, và dùng
mongorestore --oplogReplay --oplogLimitđể "chiếu lại" băng ghi hình giao dịch cho đến đúng thời điểm trước khi thảm họa xảy ra.
Thực hành:
Tạo một bảng dữ liệu, insert 10 record. Tiến hành backup.
Insert thêm 5 record mới (ghi lại timestamp). Sau đó cố tình chạy lệnh
db.collection.drop().Trích xuất đoạn Oplog chứa 5 record mới, và thực hiện PITR để khôi phục lại toàn bộ 15 record, loại bỏ lệnh drop.
Bài tập:
Viết kiến trúc hệ thống phục hồi thảm họa (Disaster Recovery Architecture) cho hệ thống. Đảm bảo RPO (Recovery Point Objective - lượng dữ liệu tối đa chấp nhận mất) nhỏ hơn 5 phút.
Nếu một file dữ liệu vật lý của WiredTiger bị hỏng (corrupted page) do lỗi RAM của máy chủ, trình bày cách sử dụng tham số
repaircủa mongod để cứu vãn cấu trúc B-Tree, và rủi ro mất dữ liệu ngầm (Silent Data Corruption) đi kèm.
Ngày 53: Vật lý của Backup phân tán & Cứu hộ Sharded Cluster
Sao lưu một Sharded Cluster không phải là chạy lệnh trên từng node riêng lẻ. Nếu Shard 1 được snapshot vào 02:00:00 nhưng Shard 2 lại snapshot vào 02:00:05, các giao dịch chuyển tiền liên phân mảnh (cross-shard) trong 5 giây đó sẽ bị rách nát (torn transaction) khi phục hồi.
Lý thuyết:
Global Consistency (Tính nhất quán toàn cục): Khái niệm phân bố thời gian (Clock skew) giữa các máy chủ. Làm sao để bắt toàn bộ cụm Config Servers và các Shards dừng lại tại đúng một thời điểm Oplog?
Quy trình Snapshot đồng thời: (1) Dừng Balancer -> (2) Chạy
db.fsyncLock()đồng loạt trên Secondary của Config Servers và mọi Shards -> (3) Gọi API chụp đĩa cứng (như AWS EBS) -> (4)db.fsyncUnlock()-> (5) Bật lại Balancer.Khôi phục Sharded Cluster: Trình tự khởi động sinh tử: Config Servers khởi động trước -> Cấp phát lại IP/DNS -> Khởi động Shards -> Cập nhật Metadata trên CSRS để nhận diện hạ tầng mới.
Thực hành:
Viết một kịch bản (Bash/Python) tự động hóa quy trình lock/unlock trên toàn bộ các shard. Nếu chạy trên máy Mac (như Mac mini/Air) cục bộ, bạn có thể mô phỏng bằng cách gọi hàm lock, tạo một bản copy thư mục data vật lý, rồi unlock.
Thực hiện quy trình khôi phục toàn cục từ các bản copy vật lý đó lên một hạ tầng mạng nội bộ ảo mới (Docker network khác).
Bài tập:
Trong quá trình hệ thống đang bị
fsyncLock, luồng giao dịch chuyển tiền ghi vào Node Primary có bị thất bại không? Giải thích cơ chế hàng đợi (Queue) I/O tại tầng RAM của WiredTiger lúc này.Tại sao việc phục hồi (Restore) một Sharded Cluster từ
mongodumplại mất nhiều ngày đối với database Terabyte, và gây hiện tượng "Oplog Rollover" (tràn Oplog) đánh sập các node phụ?
Ngày 54: Observability Internals & Telemetry Architecture
Hệ thống giám sát (Monitoring) không chỉ là nhìn biểu đồ CPU. Khi làm kiến trúc sư, bạn phải biết chính xác query nào đang kẹt ở khâu lấy Lock, khâu quét đĩa, hay khâu gửi mạng.
Lý thuyết:
Cấu trúc
system.profile: Bóc tách BSON document của một truy vấn chậm. Hiểu ý nghĩa các trường:locks,yields(số lần nhường CPU),docsExamined(số bản ghi phải quét),keysExamined.Database Profiler vs. Log level: Đánh đổi (Trade-off) hiệu năng. Tại sao bật Profiler level 2 (bắt mọi query) lại làm tăng vọt I/O trên đĩa cứng?
Prometheus & OpenTelemetry: Kiến trúc xuất log (Exporter) từ MongoDB ra các hệ thống metric tập trung.
Thực hành:
Thiết lập
slowOpThresholdMsxuống 50ms và bật Profiler level 1.Cài đặt MongoDB Exporter và kết nối với Grafana. Thiết lập phân quyền gắt gao (RBAC) trên Grafana sao cho chỉ team Ops mới có quyền xem các Dashboard chứa log truy vấn nhạy cảm để tuân thủ chuẩn ngân hàng.
Bài tập:
Đọc một document từ bảng
system.profile, hãy tính toán tỷ lệdocsExamined / nreturned. Nếu tỷ lệ này > 1000, hệ thống của bạn đang mắc lỗi gì về Data Modeling hoặc Indexing?Trình bày phương án cấu hình
mongodđể xuất log dưới dạng JSON (systemLog.logAppend: truevàsystemLog.destination: file) rồi dùng Filebeat đẩy thẳng vào Elasticsearch để truy vết mà không cần bật Profiler.
Ngày 55: Text Search Internals (Self-Managed) & Inverted Indexes
Tìm kiếm text (ví dụ: tìm tên khách hàng, nội dung chuyển tiền) bằng Regex ($regex) là một thảm họa Collection Scan. Nhưng Text Index tự quản lý của MongoDB cũng có những giới hạn phần cứng nghiêm trọng.
Lý thuyết :
Inverted Index (Chỉ mục đảo ngược): Khác biệt hoàn toàn so với B-Tree. Cách engine băm nhỏ (Tokenization) câu "Chuyển tiền lương tháng 10" thành các token độc lập và ánh xạ ngược lại
_idcủa document chứa nó.Stemming & Stop words: Quá trình loại bỏ các từ vô nghĩa (như "là", "và") và đưa từ về dạng gốc.
Scoring (TF-IDF): Thuật toán Term Frequency-Inverse Document Frequency. MongoDB tính điểm liên quan (
textScore) như thế nào để xếp hạng kết quả?Những giới hạn chết người: Tại sao một collection chỉ được có MỘT
\(textindex? Tại sao câu lệnh kết hợp\)match(trên trường thường) và$textlại không thể tận dụng Index tối ưu?
Thực hành:
Tạo một Text Index trên trường
descriptionvàbeneficiary_name.Thực hiện truy vấn
$texttìm kiếm cụm từ chính xác (Enclosed in quotes) và loại trừ từ khóa (Dấu-).Sắp xếp kết quả trả về dựa trên
$meta: "textScore".
Bài tập:
Thiết kế của MongoDB yêu cầu Text Index phải nạp một lượng lớn RAM. Tính toán chi phí Working Set Size khi bạn tạo Text Index trên một bộ dữ liệu 50 triệu giao dịch.
Giải thích tại sao khi sử dụng Collation (hỗ trợ phân biệt dấu/không dấu tiếng Việt) trên B-Tree Index lại hiệu quả hơn Text Index đối với bài toán "Tìm chính xác tên người dùng".
Ngày 56: Atlas Search Architecture (Lucene) & Complex Pipelines
Để vượt qua giới hạn của Text Index tự quản lý, Atlas Search tích hợp thẳng Apache Lucene vào MongoDB. Kiến trúc này giải quyết được cả bài toán độ nhạy (Fuzzy) và khả năng mở rộng.
Lý thuyết:
Kiến trúc
mongot(MongoDB Text process): Đây là một tiến trình hoàn toàn tách biệt chạy song song vớimongod(trên RAM dùng Java/JVM). Nó sử dụng Change Streams để đồng bộ dữ liệu từmongodsang Lucene một cách gần như thời gian thực.Analyzers, Tokenizers & Filters: Cách cấu hình một custom Analyzer. Ví dụ: Dùng
nGramtokenizer để tìm kiếm một phần của tên, hoặcedgeGramcho tính năng Autocomplete.Fuzzy Matching (Tìm kiếm mờ): Thuật toán Levenshtein Distance. Tìm ra kết quả đúng dù người dùng gõ sai chính tả (ví dụ gõ "Ngyễn Văn A" thay vì "Nguyễn Văn A").
Thực hành:
Tạo một cụm Atlas miễn phí, định nghĩa một Atlas Search Index cấu hình custom Analyzer sử dụng
lucene.vietnameseđể loại bỏ dấu câu và stop words tiếng Việt một cách chuẩn xác.Viết một Aggregation Pipeline sử dụng stage
$search(BẮT BUỘC phải là stage đầu tiên trong pipeline) kết hợp vớicompound(must, should, mustNot) để mô phỏng tính năng tra cứu lịch sử nâng cao.
Bài tập:
Theo dõi độ trễ đồng bộ (Replication Lag) giữa tiến trình
mongodvà tiến trìnhmongot. Đề xuất cách xử lý ở tầng Application khi vừa insert một giao dịch xong nhưng kết quả$searchngay sau đó lại chưa hiển thị.Sự khác biệt về mặt sử dụng bộ nhớ (Heap Memory của JVM) khi bạn thiết lập tính năng Autocomplete (sử dụng EdgeNgrams) so với tìm kiếm tiêu chuẩn là gì?
Ngày 57: Tương lai của Dữ liệu: Vector Search & GenAI Integration
Không dừng lại ở Text, các hệ thống hiện đại phải hiểu được "ngữ nghĩa" (Semantic). Đây là bước đệm hoàn hảo để tích hợp Database lõi với các mô hình Generative AI.
Lý thuyết:
Vector Embeddings: Quá trình biến đổi văn bản (ví dụ: mô tả hành vi giao dịch) thành các mảng số float nhiều chiều (ví dụ 1536 chiều của OpenAI).
Thuật toán HNSW (Hierarchical Navigable Small World): Cấu trúc đồ thị nhiều lớp cho phép tìm kiếm láng giềng gần nhất (ANN - Approximate Nearest Neighbors) trong không gian Vector với tốc độ O(log n) thay vì quét toàn bộ (Brute Force KNN).
Stage
$vectorSearch: Kiến trúc kết hợp giữa lọc truyền thống (Pre-filtering) và tìm kiếm Vector trên cùng một engine để đảm bảo hiệu năng và độ chính xác.
Thực hành:
Viết một script Python tính toán vector embeddings (sử dụng một thư viện NLP cơ bản hoặc gọi API mô hình nhúng), sau đó cập nhật mảng vector này vào các documents tương ứng.
Tạo một Vector Index cho trường vừa tạo và dùng
$vectorSearchđể thực thi truy vấn tìm các bản ghi "có ý nghĩa tương đồng".
Bài tập:
Trong nghiệp vụ ngân hàng (AML/Fraud Detection), hãy phác thảo một kiến trúc sử dụng
$vectorSearchđể phát hiện các giao dịch đáng ngờ dựa trên sự tương đồng về ngữ nghĩa trong các ghi chú chuyển tiền, thay vì chỉ dùng rules (luật) cứng.Đánh giá chi phí RAM và băng thông mạng khi lưu trữ hàng triệu Vector Embeddings 1536 chiều trong MongoDB. Làm sao để sử dụng tính năng nén lượng tử (Quantization) để giảm footprint của Vector Index?
Ngày 58: Geospatial Internals (S2 Geometry) & Thuật toán chống gian lận (Fraud Detection)
Trong tài chính, truy vấn địa lý không chỉ để "tìm nhà hàng", mà là cốt lõi của các hệ thống Anti-Fraud (Ví dụ: Velocity Checks - Kiểm tra tốc độ di chuyển bất khả thi).
Lý thuyết:
Kiến trúc
2dsphereIndex dưới vỏ bọc: MongoDB sử dụng thư viện S2 Geometry của Google. Cách nó chia bề mặt Trái Đất thành một lưới các khối (cells) và gán cho mỗi khối một ID 64-bit dựa trên đường cong Hilbert (Hilbert space-filling curve).B-Tree cho tọa độ không gian: Khi bạn lưu một tọa độ GeoJSON, MongoDB băm nó thành mảng các S2 Cell IDs ở nhiều độ phân giải khác nhau và nhét vào B-Tree.
Bài toán Velocity Fraud: Phát hiện một tài khoản vừa đăng nhập thực hiện giao dịch ở Hà Nội, nhưng 5 phút sau lại có giao dịch ở TP.HCM.
Thực hành:
Tạo collection
device_logslưu trữuser_id,timestamp, vàlocation(GeoJSON). Đánh index2dsphere.Viết một Aggregation Pipeline sử dụng
$geoNearkết hợp với tính toán chênh lệch thời gian để phát ra cảnh báo (Alert) nếu tốc độ di chuyển giữa 2 điểm giao dịch vượt quá 1000 km/h.
Bài tập:
So sánh độ chính xác và chi phí RAM giữa Index
2d(cho mặt phẳng) và2dsphere(cho hình cầu Trái Đất). Tại sao tuyệt đối không dùng2dcho tính toán khoảng cách hàng không?Nếu một khách hàng đang di chuyển liên tục, việc liên tục update tọa độ GPS vào một document duy nhất có gây ra hiện tượng gì ở tầng Storage? Trình bày cách dùng Bucket Pattern để lưu vết (Tracking).
Ngày 59: Jepsen Tests & Linearizability dưới lăng kính hệ thống phân tán
Hầu hết lập trình viên tin tưởng tuyệt đối vào cơ sở dữ liệu cho đến khi họ đọc các báo cáo của Jepsen. Hệ thống của bạn phải chịu được sự gián đoạn mạng ngẫu nhiên mà không rách dữ liệu.
Lý thuyết:
Jepsen Framework & Phân tích lỗi: Cách công cụ Jepsen tìm ra các lỗi Stale Reads, Dirty Writes trong các phiên bản MongoDB cũ, và cách MongoDB sửa chúng bằng Raft-based consensus.
Linearizability (Tính tuyến tính): Mức độ nhất quán mạnh nhất. Bất kỳ thao tác đọc nào cũng phải nhìn thấy thao tác ghi thành công gần nhất trên toàn hệ thống.
Causal Consistency (Đồng nhất nhân quả) trong thực tế: Sử dụng
clusterTimeđể đảm bảo luồng nghiệp vụ: Giao dịch được duyệt -> Sinh log hạch toán -> Bắn notification không bao giờ bị sai thứ tự ngay cả khi đọc từ Node Secondary.
Thực hành:
Viết một kịch bản Python mở nhiều luồng (threads), liên tục ghi và đọc với các cấu hình Read/Write Concern khác nhau (
localvsmajorityvslinearizable).Mô phỏng Network Partition (dùng
iptablesngắt mạng Node Primary). Ghi log lại các dữ liệu đọc bị "Stale" (cũ) khi hệ thống đang bầu chọn lại.
Bài tập:
Phân tích chi phí độ trễ (Latency Cost) khi cấu hình Read Concern là
linearizable. Tại sao Primary lại buộc phải ping đa số các thành viên khác trước khi trả kết quả đọc cho Client?Nếu API kiểm tra hạn mức (Limit Check) đọc ra dữ liệu cũ (Stale Read), công ty có thể bị thất thoát tiền. Đề xuất tổ hợp Read/Write Concern bắt buộc cho API này.
Ngày 60: CloudOps, Kubernetes Operator & Mạng eBPF (Cilium)
Với vai trò Platform Engineer, việc vận hành database stateful không thể dùng Bash script thủ công. Phải đẩy mọi thứ lên kiến trúc Cloud-Native với K8s.
Lý thuyết:
StatefulSets & Storage Classes: Khác biệt giữa Deployment và StatefulSet. Cách Pod MongoDB tự động bind (gắn) với Persistent Volumes (EBS/NVMe) qua PVC.
MongoDB Enterprise Operator: Pattern Operator trên Kubernetes. Cách Operator tự động hóa việc khởi tạo Replica Set, thay đổi chứng chỉ TLS, và tự động nâng cấp phiên bản (Rolling Update).
Bảo mật mạng với eBPF (Cilium CNI): Không dùng Firewall truyền thống. Áp dụng Cilium Network Policies ở Layer 7 để kiểm soát: Chỉ Pod có nhãn (label)
app=coremới được phép gọi lệnhinsertvào port 27017, các Pod khác chỉ đượcfind.
Thực hành:
Sử dụng kiến trúc ARM/M-series cục bộ (Mac mini/Air), dựng một cụm K8s mini (như Kind hoặc Minikube).
Triển khai MongoDB Community/Enterprise Operator, tạo một cụm Replica Set 3 node.
Áp dụng Cilium Network Policy chặn toàn bộ traffic ngoại trừ Pod API đã được định danh.
Bài tập:
Khi một Pod MongoDB (Primary) bị Kubernetes eviction (trục xuất) do Node cạn tài nguyên (Memory Pressure), Operator và MongoDB Raft sẽ phối hợp với nhau như thế nào để hệ thống không bị downtime?
Trình bày file cấu hình YAML của Cilium Network Policy để giới hạn (Rate Limit) số lượng kết nối tối đa vào DB chống lại thảm họa Connection Storms.
Ngày 61: Di chuyển hệ thống lõi (Core Migration) & Change Data Capture
Một trong những nhiệm vụ khó nhất của Solution Architect là chuyển đổi dữ liệu từ hệ thống ngân hàng lõi cũ sang kiến trúc mới mà không được phép ngừng dịch vụ.
Lý thuyết:
The Strangler Fig Pattern: Tách dần từng module khỏi hệ thống cũ (Monolith) sang hệ thống mới (Microservices + MongoDB).
Kiến trúc Change Data Capture (CDC): Bắt sự kiện thay đổi dữ liệu từ hệ thống nguồn (ví dụ: Oracle/T24 SQL) thông qua Debezium và Kafka, sau đó stream (bơm) sang MongoDB dưới dạng JSON document.
Dual Writes vs CDC: Tại sao việc viết code "ghi đồng thời vào cả 2 DB" lại là Anti-pattern và cực kỳ dễ sinh ra lỗi dữ liệu không đồng nhất (Inconsistency).
Thực hành:
Dựng Kafka, Zookeeper, và Kafka Connect.
Cấu hình MongoDB Kafka Source/Sink Connector. Bơm một luồng dữ liệu giả lập (mock data) liên tục từ Kafka vào bảng giao dịch của MongoDB.
Bài tập:
Quá trình hạch toán có thể sinh ra hàng triệu bản ghi thay đổi số dư trong 1 giờ. Nếu tốc độ ghi của MongoDB Sink Connector không theo kịp tốc độ của Kafka, hiện tượng Lag xảy ra. Bạn cấu hình Bulk Write và Batch Sizing như thế nào trên Connector để tối ưu I/O?
Giải thích cơ chế chuyển đổi dữ liệu phẳng (Flat relational rows) thành dữ liệu lồng nhau (Nested BSON Documents) khi luồng dữ liệu đi qua Kafka Stream.
Ngày 62: System Design
Lý thuyết:
Thiết kế Domain-Driven Design (DDD) cho MongoDB: Tách Bounded Context cho mảng Chuyển tiền quốc tế.
Outbox Pattern: Cách giải quyết bài toán: "Vừa phải lưu giao dịch vào MongoDB, vừa phải gọi API sang mạng Swift/Ripple". Dùng Change Streams để lắng nghe collection
outboxvà trigger lời gọi API bất đồng bộ, đảm bảo tính ACID nội bộ.
Thực hành :
Vẽ sơ đồ kiến trúc tổng thể (Architecture Diagram).
Viết Schema Design chi tiết bằng JSON Schema cho các collections:
customers,wallets,transactions,compliance_logs.
Bài tập:
Với yêu cầu hệ thống đạt 10.000 TPS ghi giao dịch và 50.000 TPS truy vấn số dư. Hãy thiết kế chiến lược Sharding (chọn Shard Key) cho bảng
transactionsđể tránh Hotspot, đồng thời đảm bảo bảngwalletskhông bị khóa (Lock contention) khi tính toán số dư.Thiết kế luồng xử lý lỗi (Compensating Transaction) khi tiền đã trừ ở MongoDB nhưng API sang hệ thống đối tác bị timeout.
Ngày 63: Chaos Engineering & System Tracing với eBPF
Hệ thống CloudOps phải được thử lửa. Nếu bạn không tự phá hệ thống của mình, sự cố Production sẽ làm điều đó thay bạn.
Lý thuyết:
Chaos Engineering: Phương pháp luận cố tình gây lỗi (tiêm độ trễ mạng, tắt ngẫu nhiên tiến trình, lấp đầy ổ cứng) để xem hệ thống tự phục hồi ra sao.
Linux Kernel Tracing: Sử dụng công cụ
bcc(BPF Compiler Collection) để theo dõi các system calls ở tầng hạt nhân mà không làm chậm server.Phân tích I/O Latency: Tracing lời gọi
fsync()vàpwritev()của WiredTiger để xem ổ cứng có thực sự đang bị nghẽn (bottleneck) hay không.
Thực hành:
Cài đặt Chaos Mesh hoặc LitmusChaos trên cụm Kubernetes.
Tiêm một lỗi
NetworkChaos(thêm độ trễ 500ms vào mọi gói tin) giữa các Pod MongoDB và quan sát xem hàng đợi Connection Pool của ứng dụng có bị sập không.
Bài tập:
Viết một lệnh/script sử dụng eBPF (như
biosnoophoặcbiolatency) để bắt và thống kê thời gian phản hồi của đĩa NVMe khi MongoDB thực hiện Checkpoint mỗi 60 giây.Khi một Pod Config Server bị xóa ngẫu nhiên (Chaos kill), cụm
mongosphản ứng thế nào với các truy vấn đang bay trên mạng (In-flight requests)?
Ngày 64: AI Integration & Model Context Protocol (MCP)
Tương lai của quản trị dữ liệu là khả năng giao tiếp ngữ nghĩa với Database. Các mô hình GenAI cần một lớp giao thức an toàn để tương tác với Schema.
Lý thuyết:
Model Context Protocol (MCP): Kiến trúc cho phép các trợ lý AI (Claude, Gemini) an toàn truy cập vào môi trường cục bộ (file, database) để lấy ngữ cảnh.
Bảo mật GenAI (Prompt Injection Defense): Làm sao để AI chỉ có thể đọc Schema Design Patterns hoặc Query Performance Metrics mà tuyệt đối không được phép sinh ra các lệnh
drop()hayupdate()?Vector Search Recap: Kết hợp metadata truyền thống và Semantic Search để tạo ra hệ thống Recommendation nội bộ cho Ops team.
Thực hành:
Viết một MCP Server bằng Python/TypeScript. Cung cấp một Tool cho phép AI query cấu trúc JSON Schema hiện tại của bảng
transactions(sử dụng lệnhdb.getCollectionInfos()).Khởi chạy AI client (Cursor hoặc Claude Desktop), kết nối qua MCP và yêu cầu AI: "Hãy phân tích Schema hiện tại và đề xuất các điểm có thể áp dụng Extended Reference".
Bài tập:
Thiết kế một hệ thống "Auto-Tuning Database" nội bộ: Log chậm (Slow Query) được đẩy qua Change Streams -> Kích hoạt MCP Server -> Gọi LLM phân tích plan thực thi (
explain) -> Đề xuất Index tối ưu cho DBA. Trình bày luồng kiến trúc.Đánh giá rủi ro lộ lọt dữ liệu PII (Personally Identifiable Information) khi cấp quyền cho LLM đọc các documents mẫu thông qua MCP, và giải pháp Data Masking.
Ngày 65: Phân tích Source Code lõi
Lý thuyết:
Kiến trúc Source Code MongoDB (C/C++): Dạo quanh kho lưu trữ mã nguồn mở của MongoDB. Cách tìm kiếm các định nghĩa về Memory Allocator, B-Tree implementation và Raft consensus trong folder
src/mongo.Kỹ năng chia sẻ (Speaker Prep): Cách đóng gói các kiến thức nội bộ phức tạp thành các Session công nghệ dễ hiểu nhưng chuyên sâu (Ví dụ: Cho các sự kiện Recap hoặc Tech Talk nội bộ).
Xây dựng Profile chuyên gia: Đóng góp vào tài liệu, viết các custom plugins hoặc tools giám sát hiệu năng riêng biệt, chia sẻ cách giải quyết các "ca khó" về Database Defense Mechanisms.
Thực hành:
Clone source code MongoDB từ GitHub. Tìm file định nghĩa hằng số giới hạn 16MB của BSON document và file quản lý giới hạn 100MB RAM của Aggregation Pipeline.
Xây dựng một dàn bài (Outline) chi tiết gồm 15 slide về chủ đề: "Schema Design Patterns & Tối ưu WiredTiger cho kiến trúc.
Bài tập:
- Trình bày quy trình theo dõi và phân tích Release Notes của MongoDB mỗi khi có phiên bản vá lỗi (Patch Release) để đánh giá xem có cần thiết phải cập nhật hệ thống Production hay không.