Lưu trữ mạng — block, file hay object?
iSCSI, NFS/SMB và S3 khác nhau ở tầng nào, thêm bao nhiêu độ trễ, và giả định POSIX nào vỡ khi ổ đĩa của bạn thật ra nằm sau một sợi cáp mạng.
TL;DR: Block, file và object không phải ba sản phẩm cạnh tranh mà là ba tầng trừu tượng khác nhau: block (iSCSI) đưa cho bạn một ổ đĩa thô để tự đặt filesystem lên, file (NFS/SMB) đưa cho bạn một cây thư mục do server quản lý, còn object (S3) đưa cho bạn một kho khoá-giá trị không có thư mục thật. Câu hỏi quyết định khi chọn không phải "cái nào nhanh hơn" mà là giả định POSIX nào sẽ vỡ — rename nguyên tử, ghi đè một phần, khoá file, và thời điểm client khác nhìn thấy dữ liệu mới.
Nhóm bạn chuyển một service từ ổ đĩa cục bộ sang lưu trữ dùng chung để nhiều máy cùng truy cập. Code không đổi một dòng, test qua hết. Lên production hai tuần thì xuất hiện những file cấu hình rỗng hoặc cụt giữa chừng — đúng những file service ghi bằng pattern ghi tạm rồi đổi tên, cái pattern vốn được coi là an toàn tuyệt đối.
Không có gì sai trong code. Cái sai là nó được viết dựa trên một tập giả định mà filesystem cục bộ luôn thoả mãn, còn lưu trữ mạng thì không nhất thiết.
1. Analogy — ba cách gửi đồ ở nơi khác
Bạn hết chỗ chứa đồ và phải gửi ra ngoài. Có ba kiểu dịch vụ.
Kiểu thứ nhất cho thuê nguyên một kho trống: xếp thế nào, đánh số ra sao là việc của bạn. Người cho thuê chỉ đảm bảo bốn bức tường, không quan tâm bên trong có gì.
Kiểu thứ hai là kho có thủ thư. Bạn đưa đồ kèm tên, thủ thư xếp lên kệ và ghi sổ; muốn lấy thì đọc tên. Bạn không tự vào kho và cũng chẳng cần biết nó xếp theo hệ thống nào.
Kiểu thứ ba là kho tự động: nhét đồ vào cùng một mã, đọc mã thì máy trả lại đúng gói. Nhưng ở đây không kệ, không ngăn, không thư mục — chỉ có mã và gói, nên sửa một chi tiết trong gói là phải làm lại cả gói.
| Kiểu gửi đồ | Trong hệ thống |
|---|---|
| Thuê kho trống, tự xếp | Block storage (iSCSI) — bạn nhận ổ thô, tự đặt filesystem |
| Kho có thủ thư ghi sổ | File storage (NFS/SMB) — server giữ filesystem, bạn thấy cây thư mục |
| Kho tự động theo mã | Object storage (S3) — kho khoá-giá trị, không có thư mục thật |
| Không sửa được một chi tiết, phải thay cả gói | Object không ghi đè một phần — bạn PUT lại toàn bộ |
| Thủ thư chỉ cập nhật sổ khi bạn ký nhận xong | Ngữ nghĩa close-to-open của NFS |
Hỏi một câu là phân biệt được ngay: ai giữ filesystem? Bạn giữ thì đó là block. Server giữ thì đó là file. Không ai giữ, vì không có filesystem, thì đó là object.
2. Ba tầng trừu tượng, ba thứ bạn nhận được

Block giao cho bạn một dãy khối đánh số, y hệt ổ đĩa cắm trong máy: bạn mkfs lên đó rồi mount như bình thường. Vì filesystem chạy trên máy bạn, mọi ngữ nghĩa POSIX giữ nguyên, kể cả khoá file và rename nguyên tử. Cái giá là chỉ một máy được mount đọc-ghi cùng lúc.
File đặt filesystem ở phía server, nên nhiều máy dùng chung được — đó chính là lý do NFS tồn tại. Đổi lại, mỗi thao tác là một lượt đi về qua mạng, và server phải giả lập những đảm bảo mà filesystem cục bộ cho không.
Object bỏ hẳn khái niệm filesystem: không inode, không thư mục, không con trỏ vị trí trong file. Chỉ có khoá và giá trị. Dấu gạch chéo trong tên khoá thuần tuý là quy ước hiển thị — bên dưới nó chỉ là một ký tự trong chuỗi.
Mọi thao tác trong bài này đều phải chui qua ngăn xếp mạng bạn đã học. Điều đó đặt một sàn cứng cho độ trễ: dù thiết bị lưu trữ ở đầu kia nhanh cỡ nào, bạn vẫn phải trả ít nhất một lượt đi về. Với thao tác nhỏ và nhiều, chính khoản đi về đó mới là thứ quyết định, chứ không phải tốc độ ổ đĩa.
3. Giả định POSIX nào vỡ khi ổ đĩa nằm sau sợi cáp mạng?
Đây là câu hỏi quan trọng nhất của bài, và cũng là thứ hầu như không tài liệu so sánh nào nói tới.
Rename nguyên tử. Trên filesystem cục bộ, rename là một thao tác không thể chia đôi: người đọc thấy hoặc file cũ nguyên vẹn, hoặc file mới nguyên vẹn. Trên object storage thì không có rename. Lệnh mv mà công cụ dòng lệnh cung cấp thực chất là PUT khoá mới rồi DELETE khoá cũ — hai request riêng biệt, và giữa chúng có một khoảng thời gian mà cả hai khoá cùng tồn tại, hoặc tệ hơn, request thứ hai thất bại và bạn còn lại hai bản.
Ghi đè một phần. Sửa 4 KB giữa một file 2 GB là chuyện thường trên filesystem. Trên object storage, đơn vị là cả object — đổi một byte thì PUT lại toàn bộ. Đó là lý do không ai đặt file dữ liệu database lên object storage một cách ngây thơ.
Khoá file. flock qua mạng rất mong manh: server phải giữ trạng thái khoá và phải biết client nào đã chết để nhả. Mất mạng vài giây là đủ sinh ra cảnh hai bên cùng tin mình đang giữ khoá.
Thời điểm bên khác nhìn thấy dữ liệu. NFS dùng ngữ nghĩa close-to-open: bên ghi đóng file, bên đọc mở lại, thì mới chắc chắn thấy nội dung mới.
Nhiều tài liệu trên mạng vẫn nói S3 là eventual consistency. Điều đó không còn đúng: từ năm 2020, S3 cung cấp strong read-after-write — ghi xong đọc lại là thấy. Nhưng đừng suy rộng từ đó ra: nhất quán khi đọc một khoá không đồng nghĩa với có transaction nhiều khoá, có rename nguyên tử, hay ghi đè được một phần. Ba thứ sau vẫn không có.
Quay lại sự cố đầu bài: pattern ghi tạm rồi đổi tên vỡ chính vì nó dựa vào rename nguyên tử — thứ mà tầng lưu trữ mới không hứa.
4. Chọn kiểu nào cho workload nào
Ba tình huống dưới đây. Với mỗi cái, tự chọn block, file hay object, và viết ra lý do trước khi đọc phân tích ở dưới. Lý do quan trọng hơn đáp án.
- Database quan hệ ghi nặng, một instance duy nhất.
- Hai chục máy chủ ứng dụng cùng đọc một bộ file cấu hình và tài nguyên tĩnh.
- Kho ảnh người dùng tải lên, hàng chục terabyte, chỉ ghi một lần rồi đọc nhiều lần.
Tình huống 1 — block. Database cần ghi đè tại chỗ, cần khoá, cần fsync có ngữ nghĩa chặt, và nó chỉ chạy trên một máy. Đúng mô tả của block storage. Đặt lên NFS thì bạn giao phó ngữ nghĩa fsync cho một server ở xa; đặt lên object thì ghi đè một phần còn chẳng tồn tại.
Tình huống 2 — file. Nhiều máy cùng đọc đúng là thứ file storage sinh ra để làm, và cây thư mục cho phép ứng dụng giữ nguyên đường dẫn quen thuộc. Chỉ cần nhớ close-to-open: sau khi cập nhật, bên đọc phải mở lại file mới chắc chắn thấy bản mới.
Tình huống 3 — object. Ghi một lần đọc nhiều lần, không sửa tại chỗ, dung lượng lớn và cần rẻ. Object khớp hoàn toàn, còn những thứ nó thiếu thì workload này vốn không dùng tới.
| Bạn cần | Nên chọn | Giả định phải từ bỏ |
|---|---|---|
| Ghi đè tại chỗ, khoá, một máy | Block | Nhiều máy cùng mount đọc-ghi |
| Nhiều máy cùng đọc, đường dẫn quen thuộc | File | Thấy thay đổi tức thì giữa các máy |
| Dung lượng lớn, ghi một lần đọc nhiều | Object | Rename nguyên tử, ghi đè một phần, thư mục thật |
5. Pitfall của riêng concept này
❌ Nhầm 1 — coi tiền tố có dấu gạch chéo trong S3 là thư mục thật.
✅ Khoá anh/2026/08/a.jpg là chuỗi phẳng, dấu gạch chéo chỉ là ký tự. Không có thao tác "đổi tên thư mục" — đổi tiền tố cho một triệu object là chép và xoá một triệu lần.
❌ Nhầm 2 — bê pattern ghi tạm rồi đổi tên sang lưu trữ mạng mà không xem lại.
✅ Pattern đó dựa vào rename nguyên tử trong cùng một filesystem. Trên object storage nó không tồn tại; trên NFS thì có nhưng còn phải tính client khác nhìn thấy lúc nào. Cách thay thế: ghi khoá mới có phiên bản, rồi cập nhật một con trỏ duy nhất trỏ tới bản hiện hành.
❌ Nhầm 3 — mount cùng một volume block cho nhiều máy để "dùng chung cho tiện".
✅ Filesystem giả định nó là chủ sở hữu duy nhất của thiết bị và giữ trạng thái trong bộ nhớ máy đó. Hai máy cùng mount đọc-ghi thì hỏng dữ liệu chứ không phải chậm. Muốn nhiều máy cùng ghi thì dùng filesystem thiết kế riêng cho việc đó, hoặc chuyển lên tầng file.
6. 📚 Đào sâu (tuỳ chọn)
- Amazon S3 — User Guide — mục về mô hình nhất quán, nơi ghi rõ phạm vi của bảo đảm strong read-after-write và những gì nằm ngoài phạm vi đó.
Ghi chú: phần ngữ nghĩa close-to-open của NFS trong bài này được trình bày ở mức cơ chế; nếu bạn cần chính xác tới từng trường hợp biên thì đọc thẳng man 5 nfs trên máy của mình, vì hành vi phụ thuộc phiên bản giao thức và tuỳ chọn mount.
7. Liên hệ các bài khác
- fsync và độ bền — qua mạng, khoản chờ xác nhận của
fsynccộng thêm cả một lượt đi về. - Filesystem — từ tên file tới khối dữ liệu — nắm inode và directory entry rồi mới thấy rõ object storage đã bỏ đi chính xác những gì.
- Tuần tự vs ngẫu nhiên — mỗi thao tác nhỏ qua mạng phải trả thêm một lượt đi về, nên khoảng cách với thao tác lớn còn giãn rộng hơn.
- Mini-challenge — đo ổ đĩa của chính bạn — lab khép module, đo bằng tay những chênh lệch cả module đã lập luận.
8. Tóm tắt
- Ba kiểu lưu trữ mạng khác nhau ở câu hỏi "ai giữ filesystem": bạn giữ là block, server giữ là file, không ai giữ là object.
- Trục quyết định khi chọn không phải tốc độ mà là giả định nào bị từ bỏ — rename nguyên tử, ghi đè một phần, khoá file, và thời điểm bên khác thấy dữ liệu.
- S3 có strong read-after-write từ 2020, nhưng vẫn không có transaction nhiều khoá, không rename nguyên tử, không ghi đè một phần object.
- Dấu gạch chéo trong khoá object chỉ là ký tự chứ không phải thư mục, nên "đổi tên thư mục" thực chất là chép và xoá từng object một.
9. Tự kiểm tra
Q1Một service dùng pattern ghi file tạm rồi rename để cập nhật cấu hình an toàn. Chuyển sang S3 thì thỉnh thoảng client đọc được file rỗng. Giải thích cơ chế và nêu cách thay thế.▸
Pattern đó dựa vào rename nguyên tử: trên filesystem cục bộ, người đọc thấy hoặc bản cũ nguyên vẹn hoặc bản mới nguyên vẹn. S3 không có rename — mv thực ra là PUT khoá mới rồi DELETE khoá cũ, hai request riêng, giữa chúng là cửa sổ đọc trúng trạng thái dở dang.
Cách thay thế là bỏ hẳn ý tưởng đổi tên: ghi mỗi bản vào một khoá có phiên bản, rồi cập nhật một con trỏ trỏ tới bản hiện hành — thao tác ghi một khoá thì S3 bảo đảm đọc lại thấy ngay.
Q2Vì sao không thể mount cùng một volume block cho hai máy chủ để chúng dùng chung dữ liệu, trong khi NFS thì nhiều máy dùng chung được?▸
Với block storage, filesystem chạy trên máy bạn và giữ trạng thái trong bộ nhớ máy đó: bản đồ block trống, cache metadata, khoá nội bộ. Nó được viết với giả định mình là chủ sở hữu duy nhất của thiết bị. Hai máy cùng mount đọc-ghi là hai bản filesystem cùng tin mình nắm sự thật — hỏng dữ liệu, không phải chậm.
NFS đặt filesystem ở phía server nên chỉ một bên quyết định. Đánh đổi là mỗi thao tác phải qua mạng, và những đảm bảo mà filesystem cục bộ cho không thì server phải giả lập lại, thường yếu hơn.
Q3Đồng nghiệp nói: 'S3 giờ strong consistency rồi, dùng nó thay NFS cho cụm ứng dụng cũng được.' Phản biện bằng cơ chế.▸
Strong read-after-write chỉ nói về một khoá: ghi xong đọc lại khoá đó là thấy bản mới. Nó không mở rộng thành những đảm bảo mà ứng dụng viết cho filesystem đang ngầm dựa vào — không transaction nhiều khoá, không rename nguyên tử, không ghi đè một phần, không khoá file.
Nếu ứng dụng chỉ đọc và thay nguyên object thì dùng được. Nhưng nếu nó ghi vào giữa file, giữ file mở lâu, hay phối hợp nhiều tiến trình qua khoá file, đổi tầng lưu trữ đồng nghĩa với viết lại phần đó. Câu phải hỏi là ứng dụng dùng thao tác nào, không phải tầng lưu trữ nhất quán tới đâu.
Q4Ngữ nghĩa close-to-open của NFS nghĩa là gì, và nó gây bất ngờ trong tình huống nào?▸
Bảo đảm nhìn thấy dữ liệu mới được neo vào hai mốc: bên ghi đóng file, bên đọc mở lại. Giữa hai mốc đó client được phép cache nội dung để khỏi hỏi server liên tục — chính thứ giữ cho NFS còn dùng được về hiệu năng.
Bất ngờ xảy ra khi ứng dụng giữ file mở lâu mà kỳ vọng thấy thay đổi của máy khác ngay: một tiến trình theo dõi log do máy khác ghi, hay hai tiến trình phối hợp qua file trạng thái chung. Cả hai chạy đúng trên máy đơn rồi hỏng khi lên NFS, không kèm thông báo lỗi nào.
Q5Vì sao 'đổi tên một thư mục' trên object storage lại là thao tác đắt, trong khi trên filesystem nó gần như tức thời?▸
Trên filesystem, thư mục là đối tượng thật và tên của nó nằm trong một directory entry duy nhất ở thư mục cha. Đổi tên là sửa đúng một entry — nội dung bên trong không bị đụng tới vì chúng được tham chiếu qua inode, không qua đường dẫn.
Trên object storage không có thư mục: khoá anh/2026/a.jpg là chuỗi phẳng, dấu gạch chéo chỉ là ký tự. Muốn "đổi tên thư mục" thì phải liệt kê mọi khoá có tiền tố ấy, chép từng cái rồi xoá từng cái. Một triệu object là một chiến dịch, không phải một thao tác.
Bài tiếp theo: Mini-challenge — đo ổ đĩa của chính bạn
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên