Trong phần 1, chúng ta đã tìm hiểu cách triển khai MinIO Distributed Cluster với nhiều node và nhiều ổ đĩa. Trong phần 2, chúng ta sẽ đi sâu hơn vào kiến trúc lưu trữ của MinIO, bao gồm:
-
Cơ chế Erasure Coding (EC)
-
Replication trong MinIO
-
Overhead và hiệu suất lưu trữ
-
Số lượng disk/node có thể hỏng tối đa
Tổng quan nhanh trước khi vào chi tiết
Ở mức cao: MinIO trong chế độ Distributed dùng Erasure Coding (Reed–Solomon) để chia object thành nhiều data shard + parity shard, xếp các shard này vào một erasure set (một tập ổ/drives). Thông số chính bạn cần hiểu:
-
S = kích thước erasure set (số ổ trong set)
-
M = số parity shards (parity)
-
K = số data shards = S − M
MinIO cho phép đặt parity giữa0
vàS/2
(và khuyến nghị cấu hình mặc định tương đương S/2 để độ bền cao). MinIOMinIO Blog
1) Erasure Coding — cơ chế & ý nghĩa (kỹ thuật)
Reed–Solomon & tham số (k, m)
-
Khi client PUT một object, MinIO chia object thành K data shards, rồi sinh M parity shards bằng thuật toán Reed–Solomon. Tổng cộng S = K + M shards.
-
M (parity) cho biết số ổ có thể mất (về nguyên tắc) mà object vẫn có thể phục hồi (read) — vì bạn cần ít nhất K shards để reconstruct.
MinIO mặc định / gợi ý
-
MinIO (trước đây) mặc định dùng tỉ lệ K = S/2, M = S/2 (tức parity = 50%), nhưng giờ có thể cấu hình parity bất kỳ trong khoảng 0..S/2. Cấu hình parity = S/2 là cấu hình độ bền cao mà MinIO khuyến nghị.
Ví dụ số (để dễ hình dung)
-
Nếu 1 erasure set có S = 16 drives và bạn để parity M = 8 (mặc định N/2):
-
K = 8 (data shards), M = 8 (parity).
-
Mất tối đa 8 ổ và bạn vẫn có thể đọc (vì còn K=8 shards). Nhưng để vừa đọc vừa ghi an toàn (xem phần Quorum), số ổ mất được chấp nhận cho read+write sẽ khác (thường thấp hơn 1).
-
2) Erasure set — khái niệm rất quan trọng
-
Erasure set là tập các ổ (drives) mà MinIO dùng để lưu shards cho một stripe (một object). MinIO tự động nhóm drives thành các erasure set và stripe một object vào đúng 1 erasure set (không phân mảnh object ra khắp toàn bộ cluster). Vì vậy một object chỉ phụ thuộc vào drives trong set đó.
Hệ quả kiến trúc:
-
Nếu toàn bộ drives của một set bị mất — những object từng nằm trên set đó sẽ bị mất (mặc dù nhiều drives ở set khác vẫn khoẻ). Vì vậy kích thước set và cách phân bố drives qua node / rack quyết định “blast radius”.
Về kích thước set: MinIO thường tạo set có kích thước trong khoảng 4 → 16 drives (tùy triển khai); bạn thiết kế tổng số drives phải chia đều thành các set hợp lý.
3) Read quorum & Write quorum — công thức và ý nghĩa
MinIO có quorum đọc và quorum ghi (số shards / drives tối thiểu cần online để đọc/ghi an toàn cho một object). Múi quan trọng:
-
K (read quorum) = số data shards cần thiết để phục hồi object (về bản chất K = S − M). Nếu bạn có ít hơn K shards, MinIO không thể reconstruct object từ parity.
-
Write quorum thường bằng K, nhưng có 1 ngoại lệ: khi parity = S/2 (tức M = S/2, code-rate = 1/2) MinIO áp thêm +1 (write quorum = K+1) để tránh split-brain (kịch bản: nửa set và nửa set bị chia mạng, mỗi nửa ghi khác nhau). Vì vậy khi M = S/2, write quorum = K + 1.
Ứng dụng công thức (ví dụ)
-
S = 16, M = 8 ⇒ K = 8.
-
Read: cần ≥8 shards → có thể chấp nhận đến 8 ổ hỏng (read-only).
-
Write: với M = S/2, write quorum = K + 1 = 9 → vì vậy để vừa đọc & ghi bình thường bạn chỉ chịu được tối đa 7 ổ hỏng (16 − 9 = 7).
-
Tóm tắt: read-only tolerance ≥ write+read tolerance; khi bạn thiết kế HA cần chú ý con số cho write quorum vì nó quyết định dịch vụ có thể tiếp tục chấp nhận ghi hay không.
4) Bao nhiêu ổ / node có thể hỏng an toàn? (cách tính nhanh)
Công thức cơ bản cho read-only khả năng chịu lỗi = M (parity).
Công thức cho đọc + ghi (để dịch vụ vẫn có thể ghi) ≈ S − (write_quorum) where write_quorum = K (thường) hoặc K+1 (nếu M=S/2).
Ví dụ phổ biến:
-
Set S=4, M=2 (K=2) → read: có thể mất 2 ổ (vẫn đọc), write: nếu M=S/2 => write quorum = 3 → chịu tối đa 1 ổ để vẫn ghi.
-
Set S=8, M=4 (K=4) → read: mất 4 ổ vẫn read; write: write quorum=5 → chịu tối đa 3 ổ để vẫn write.
Lưu ý: đây là tính trên một erasure set. Tổng khả năng chịu lỗi toàn cluster phụ thuộc cách sets được phân bố (nhiều set = phân tán rủi ro) và rủi ro đồng thời (node/rack-level failure).
5) Hiệu năng: trade-offs (EC vs Replication)
-
EC (Erasure Coding): tiết kiệm dung lượng (ví dụ parity = 50% → usable = 50% raw), nhưng ghi đắt hơn do:
-
CPU để tính parity (encode) và decode khi reconstruct.
-
Ghi nhiều đường mạng (shards phải truyền qua mạng tới nhiều drives/nodes).
-
Với object nhỏ overhead cao hơn (metadata + nhiều PUT nhỏ).
-
-
Replication (multiple full copies): ghi nhanh hơn (kết thúc khi đủ replica ack), nhưng tốn dung lượng (ví dụ 2-way replication = 50% usable cũng giống EC:50% theo mặt dung lượng, nhưng cách tiêu tốn khác; 3-way replication tốn nhiều hơn).
MinIO khuyến nghị EC cho 1 datacenter vì tối ưu dung lượng/chi phí; dùng replication (bucket replication) cho multi-site/DR.
6) Healing / phục hồi sau lỗi (thao tác & nội dung)
Khi drive/node thất bại
Quy trình điển hình:
-
Unmount / thay drive hỏng.
-
Gắn drive mới, mount vào cùng mountpoint.
-
MinIO tự phát hiện drive mới, bắt đầu healing để rebuild shards trên drive đó — quá trình này chỉ tác động chủ yếu đến drive thay thế và có tác động hiệu năng giới hạn.
Khi MinIO heal một object?
-
MinIO có healing on GET/HEAD: khi client đọc object và nếu thiếu shards, MinIO sẽ reconstruct (dùng shards còn lại + parity) để phục vụ request và có thể sửa (heal) shards bị thiếu. Ngoài ra có object scanner chạy nền để kiểm tra & heal một số object theo lịch. Bạn có thể chủ động chạy
mc admin heal
để trigger heal.
Cẩn trọng vận hành: không can thiệp trực tiếp tới dữ liệu trên drives (di chuyển, xóa file), MinIO yêu cầu exclusive access; nếu thao tác thủ công có thể gây hỏng dữ liệu.
7) Thiết kế set / phân bố để giảm “blast radius”
Một vài nguyên tắc thực tế:
-
Đừng gom quá nhiều drives quan trọng vào cùng 1 rack. Nếu một set nằm hoàn toàn trong 1 rack và rack mất, những object trong set đó mất. Phân bố drives trong set qua nhiều node/rack sẽ tăng khả năng chịu lỗi server/rack.
-
Chia thành nhiều set nhỏ hơn (ví dụ nhiều set S=4 thay vì 1 set S=16) → mỗi object chỉ trải trên ít drives, nghĩa là khi một set mất, chỉ 1 phần dữ liệu bị ảnh hưởng (nhược điểm: tổng overhead parity trên toàn cluster có thể tăng). Quyết định này cần trade-off giữa độ bền, phạm vi mất dữ liệu và hiệu năng.
-
Không trộn drive sizes / types (tốc độ khác nhau gây bottleneck), đồng bộ firmware/driver để tránh tính không đều.
8) Tính toán dung lượng & ví dụ nhanh (công thức)
-
Raw capacity = sum(all drive capacities)
-
Usable capacity (với một set có K data, S total) = Raw × (K / S) = Raw × (1 − M/S)
-
Ví dụ S=16, M=8 → usable = Raw × (8/16) = Raw × 0.5 (50%).
-
Kế hoạch dư thừa: nếu muốn usable = X TB với parity mặc định S/2, bạn phải chuẩn bị Raw = 2 × X TB.
9) Một số lệnh vận hành hữu ích (quick ops)
-
Kiểm tra cluster:
mc admin info <alias>
-
Kiểm tra trạng thái object/shards: (xem shards missing)
mc admin object info <alias>/<bucket>/<object>
-
Trigger heal chủ động (cẩn thận, chạy trong maintenance window):
mc admin heal <alias> --recursive
-
Monitor logs & heal progress để xem IO/throughput:
mc admin service logs <alias>
mc admin top