I/O, Lưu trữ & Tài nguyên/Tuần tự vs ngẫu nhiên — cùng 1 GB, chênh trăm lần
3/26
Bài 3 / 26~12 phútLưu trữ & FilesystemMiễn phí lượt xem

Tuần tự vs ngẫu nhiên — cùng 1 GB, chênh trăm lần

Cơ chế thiết bị quyết định chênh lệch tuần tự với ngẫu nhiên, và ba chỉ số hay bị dùng lẫn nhau: IOPS, throughput, latency. Đọc đúng để ước lượng bottleneck.

TL;DR: Khoản phạt truy cập đĩa tính theo lần nhảy chỗ, không theo byte. HDD trả bằng seek cơ học và chờ đĩa quay; SSD trả bằng write amplification khi ghi ngẫu nhiên nhỏ và bỏ lỡ read-ahead khi đọc ngẫu nhiên — hai ổ, hai gốc rễ khác nhau cho cùng một hiện tượng. Cùng 1 GB dữ liệu: đọc bằng khối 4 KB tạo ra 262.144 thao tác, đọc bằng khối 1 MB chỉ tạo 1.024 — cùng byte, chênh 256 lần số lần trả phạt. Ba chỉ số IOPS, throughput và latency đo ba thứ khác nhau và không thay thế cho nhau.

Một bảng cơ sở dữ liệu cỡ 1 GB, lệnh VACUUM FULL sao chép toàn bộ sang vùng mới để thu hồi khoảng trống. Chạy lúc 3 giờ sáng, không có gì cạnh tranh: hết khoảng 2 phút. Chạy lúc 11 giờ trưa, cùng lúc hàng chục transaction đang ghi ngẫu nhiên vào các hàng rải rác khắp bảng: hết hơn 20 phút. Cùng 1 GB. Cùng ổ đĩa. Code không đổi dòng nào.

Mười lần chênh lệch đó không phải vì CPU hay RAM thiếu. VACUUM đọc liền mạch trả một lần phạt cho cả 1 GB; workload cạnh tranh buộc nó nhảy chỗ hàng trăm nghìn lần — số phạt nhân theo lần nhảy.

1. Analogy — cuộn băng và tường ô vuông

Cùng cần lấy một đoạn dữ liệu từ kho lưu trữ. Kho kiểu thứ nhất là cuộn băng: nội dung xếp nối đuôi nhau, đọc liền mạch từ một điểm thì tốt, nhảy đến giữa cuộn phải tua. Kho kiểu thứ hai là tường ô vuông từ bài trước — với đến ô nào cũng nhanh như nhau, nhưng viết lại ô đã có nghĩa phải tẩy cả khay, chép những tờ còn dùng sang khay trống rồi mới tẩy khay cũ.

Trong khoTrong ổ đĩa
Tua cuộn để nhảy đến điểm khácSeek + chờ quay — khoản phạt HDD mỗi lần nhảy track
Đọc liền mạch, không cần tuaSequential — đầu đọc đứng yên, dữ liệu chảy ra
Với đến ô nào cũng nhanh như nhauNAND flash — không seek, không rotational latency
Viết lại ô đã có = tẩy cả khayRandom write nhỏ kích hoạt write amplification (erase block bài 01)
Nhân viên kho gợi ý ô kế khi bạn đọc liền mạchRead-ahead — controller prefetch page tiếp theo khi truy cập tuần tự

Điểm đáng chú ý: hai kho trả phạt vì hai lý do khác hẳn nhau, không phải cùng lý do với cường độ khác nhau. Nhiều tài liệu gộp chúng làm một là sai — và chính sự gộp đó khiến người thiết kế hệ thống chọn sai giải pháp khi gặp bottleneck.

2. HDD — đầu đọc đứng yên là lợi thế tuyệt đối

Bài trước đã tính: ổ 7200 vòng/phút, nửa vòng trung bình 4,17 mili-giây, cộng seek ra tổng khoảng 10 mili-giây mỗi lần nhảy chỗ. Khoản phạt này trả theo lần nhảy, không theo byte.

Cung 1 GB du lieu, doc ngau nhien tung khoi 4 KB:
  1 GB = 1.048.576 KB, chia 4 KB = 262.144 thao tac
  Moi thao tac ~ 10 ms (seek + cho quay)
  Tong: 262.144 x 10 ms = 2.621 giay ~ 43 phut

Doc tuan tu:
  Mot lan seek (~10 ms), roi du lieu chay ra lien mach
  Toc do tuan tu HDD ~ 100 MB/s -> 1 GB mat ~ 10 giay

Cùng 1 GB, cùng ổ, chênh hơn 260 lần. Không phải vì thiết bị "lười" với ngẫu nhiên — mà vì số lần phạt nhân lên tuyến tính theo số lần nhảy.

Cung 1 GB doc bang khoi 1 MB va khoi 4 KB, hai so thao tac va hai moc thoi gian

Đọc tuần tự thì đầu đọc nhảy tới điểm bắt đầu đúng một lần, rồi đĩa cứ quay và dữ liệu cứ chảy ra. Hai khoản phạt seek và chờ quay chỉ trả một lần dù bạn đọc 1 KB hay 1 GB liền mạch. Đó là lý do backup và streaming dùng truy cập tuần tự — không phải vì "thứ tự tốt" theo nghĩa triết học, mà vì khoản phạt chỉ tính một lần.

3. Vì sao SSD không có bộ phận cơ học vẫn chênh tuần tự với ngẫu nhiên?

Câu hỏi hay dính sai ở chỗ này: "SSD không seek, không rotational latency — vậy ngẫu nhiên và tuần tự phải bằng nhau chứ?" Không phải vậy, nhưng gốc rễ khác hẳn HDD.

Với đọc: controller SSD chạy read-ahead — khi phát hiện pattern truy cập tuần tự, nó prefetch các page tiếp theo vào bộ đệm nội bộ trước khi bạn yêu cầu. Đọc ngẫu nhiên nhảy khắp nơi, controller dự đoán sai gần hết, prefetch vô nghĩa.

Với ghi — chỗ này mới là điểm dạy quý nhất: ghi ngẫu nhiên 4 KB rải rác đụng thẳng vào ràng buộc erase block từ bài 01. Một page nằm giữa erase block đang đầy, firmware không ghi đè tại chỗ được — phải chép những page còn dùng sang block trống khác, ghi dữ liệu mới, rồi xoá cả block cũ.

Ghi tuần tự thì ngược lại: firmware điền vào các page theo thứ tự trong một block, khi block đầy mới sang block tiếp — không cần đọc-chép-xoá, write amplification gần như bằng không.

Cùng hiện tượng, hai gốc rễ khác hẳn: HDD trả phạt bằng vật lý cơ học; SSD trả phạt bằng bất đối xứng giữa đơn vị ghi và đơn vị xoá.

Locality không chỉ giúp cache CPU — controller SSD cũng khai thác chính nguyên lý đó để dự đoán page kế tiếp khi đọc tuần tự. Cùng một tính chất của workload, hai tầng phần cứng khác nhau cùng được lợi.

4. Kích thước khối đổi cục diện — IOPS, throughput, latency

Ba chỉ số sau hay bị dùng lẫn nhau nên cần định nghĩa rõ:

IOPS (Input/Output Operations Per Second) là số thao tác I/O mỗi giây mà thiết bị xử lý được — đo tần suất, không đo lượng dữ liệu. Throughput là lượng dữ liệu truyền được mỗi giây (MB/s) — đo băng thông. Latency là thời gian từ lúc gửi yêu cầu đến lúc nhận xong một thao tác đơn lẻ — đo độ trễ.

Quan hệ: throughput ≈ IOPS × kích_thước_khối. Chính vì vậy, cùng thiết bị mà đo với kích thước khối khác nhau ra số khác nhau hoàn toàn:

Gia su thiet bi: 200.000 IOPS o khoi 4 KB
  Throughput = 200.000 x 4 KB = 800 MB/s

Cung thiet bi, do voi khoi 1 MB:
  800 MB/s / 1 MB = ~800 IOPS do duoc
  -> IOPS thap hon 250 lan, du throughput khong doi

Cùng 1 GB dữ liệu: đọc bằng khối 4 KB cần 262.144 thao tác, đọc bằng khối 1 MB chỉ cần 1.024. Nếu thiết bị bị giới hạn ở số thao tác (IOPS cap) thì khối lớn thắng áp đảo; nếu giới hạn ở băng thông thì kích thước khối không đổi được gì.

Vì sao IOPS cao vẫn có thể đi kèm latency tệ? Queue depth. Khi nhiều yêu cầu cùng xếp vào hàng, thiết bị xử lý song song — tổng thông lượng tăng, nhưng mỗi yêu cầu mới gửi vào phải chờ tới lượt mình. Thiết bị đang phục vụ 64 yêu cầu song song có tổng IOPS cao, nhưng request thứ 65 gửi vào phải đứng sau 64 request đó. Thông lượng tổng cao chứng minh thiết bị khoẻ — không chứng minh mỗi yêu cầu đơn lẻ trả về nhanh.

5. Pitfall của riêng concept này

Nhầm 1 — coi tuần tự và ngẫu nhiên là tính chất cố định của workload, không phải của số lần nhảy.

✅ Cùng một query có thể vừa tuần tự vừa ngẫu nhiên: VACUUM scan heap tuần tự, nhưng khi fetch row qua index thì nhảy ngẫu nhiên theo pointer. Điều quan trọng là số lần nhảy chỗ mỗi giây, không phải nhãn workload.

Nhầm 2 — dùng số IOPS benchmark để kết luận latency trong production.

✅ Benchmark IOPS thường chạy queue depth sâu để ép thiết bị xử lý tối đa song song — số đẹp, nhưng đó là latency của cả hàng đợi. Trong production với một luồng đơn, latency thấp hơn vì hàng gần rỗng. Ngược lại, nhiều luồng cạnh tranh có thể thấy latency tệ dù IOPS danh nghĩa cao vì mỗi request xếp sau hàng dài. Đo đúng chỉ số đúng cho từng câu hỏi.

Nhầm 3 — benchmark đọc mà quên xoá page cache, đo trúng RAM thay vì đĩa.

✅ Linux giữ dữ liệu đọc gần đây trong RAM (page cache); lần benchmark thứ hai trên cùng file thường đọc từ RAM chứ không xuống đĩa, kết quả lệch 2–3 bậc so với thực tế. Trước mỗi lần đo: echo 3 > /proc/sys/vm/drop_caches, hoặc dùng --direct=1 với fio để bypass cache hoàn toàn.

6. 📚 Đào sâu (tuỳ chọn)

📚 Nguồn kỹ thuật
  • fio documentation — tool benchmark I/O chuẩn; tham số bs (block size), iodepth, rw=randread/seqread, direct=1 để bypass page cache. Bài 07 dùng fio để kiểm chứng con số bạn vừa tính ở mục 4.
  • Linux kernel — vm.drop_caches — cách xoá page cache để benchmark xuống đến ổ đĩa thật.

7. Liên hệ các bài khác

  • Filesystem — inode và metadata — filesystem quyết định một file được đọc tuần tự hay ngẫu nhiên theo cách lưu block; fragmentation là thứ HDD ghét nhất.
  • Page cache và buffered I/O — tầng đệm RAM giữa ứng dụng và đĩa; hiểu tuần tự vs ngẫu nhiên giúp bạn biết page cache che được bao nhiêu.
  • Mini-challenge — đo ổ đĩa của chính bạn — tự chạy fio để kiểm chứng công thức throughput ≈ IOPS × kích thước khối trên phần cứng thật của bạn.
  • Cache line và locality — cùng nguyên lý locality nhưng ở tầng cache CPU; hai tầng phần cứng khác nhau cùng khai thác tính liên tục của truy cập.

8. Tóm tắt

  • HDD trả phạt theo lần nhảy: đọc tuần tự 1 GB mất khoảng 10 giây, đọc ngẫu nhiên từng khối 4 KB mất hơn 43 phút — 262.144 lần phạt cộng lại.
  • SSD không có seek nhưng vẫn chênh: đọc ngẫu nhiên làm read-ahead vô hiệu; ghi ngẫu nhiên nhỏ kích hoạt chu trình đọc-chép-xoá erase block, một gốc rễ khác hẳn HDD.
  • Throughput ≈ IOPS × kích thước khối; chọn kích thước khối phù hợp workload là quyết định quan trọng trong thiết kế I/O, không phải chỉ chọn ổ đĩa nhanh.
  • IOPS cao và latency thấp không đi cùng nhau khi queue depth sâu: đo throughput để hiểu sức chịu tải tổng, đo latency để hiểu trải nghiệm mỗi request đơn lẻ.

9. Tự kiểm tra

Tự kiểm tra
Q1
Cùng 1 GB dữ liệu trên HDD, đọc tuần tự mất khoảng 10 giây. Tính thời gian đọc ngẫu nhiên từng khối 4 KB, giả sử mỗi thao tác mất 10 mili-giây. Tại sao chênh lệch lớn như vậy dù cùng lượng dữ liệu?

1 GB = 1.048.576 KB chia khối 4 KB ra 262.144 thao tác. Mỗi thao tác 10 ms, tổng 2.621 giây — hơn 43 phút. Đọc tuần tự chỉ trả một lần seek rồi đầu đọc đứng yên.

Khoản phạt của HDD tính theo lần nhảy chỗ, không theo byte. Đọc tuần tự nhảy một lần; đọc ngẫu nhiên nhảy 262.144 lần, mỗi lần trả đủ cả seek lẫn chờ quay.

Q2
SSD không có seek, không có rotational latency. Vì sao đọc tuần tự trên SSD vẫn thường nhanh hơn đọc ngẫu nhiên?

Hai lý do, cả hai khác hẳn HDD. Đọc tuần tự: controller nhận ra pattern và prefetch page tiếp theo. Đọc ngẫu nhiên nhảy khắp nơi, dự đoán sai hầu hết, prefetch vô nghĩa.

Với ghi: ghi ngẫu nhiên 4 KB đụng vào erase block — phải đọc-chép-xoá cả block 1–8 MiB. Ghi tuần tự điền đầy block theo thứ tự, không kích hoạt chu trình đó.

Q3
Một ổ đĩa benchmark được 200.000 IOPS ở kích thước khối 4 KB. Tính throughput. Sau đó giải thích: nếu benchmark lại với khối 1 MB, IOPS đo được sẽ thay đổi thế nào?

Throughput = 200.000 × 4 KB = 800 MB/s. Nếu thiết bị giới hạn ở 800 MB/s, đo với khối 1 MB: 800 MB/s ÷ 1 MB = 800 IOPS — thấp hơn 250 lần so với kết quả 4 KB.

IOPS không phải hằng số của thiết bị — nó phụ thuộc kích thước khối. Số bạn cần là số khớp với kích thước I/O thật của workload, không phải số benchmark marketing chọn để đẹp.

Q4
Một hệ thống đo được tổng IOPS rất cao với queue depth 64, nhưng mỗi API request vẫn thấy latency cao. Giải thích tại sao IOPS tổng cao không đảm bảo latency thấp cho từng request.

Queue depth 64 nghĩa là 64 yêu cầu cùng chờ xử lý song song — tổng thông lượng cao, nhưng yêu cầu thứ 65 gửi vào phải xếp sau 64 cái đó. Thời gian chờ trong hàng cộng thẳng vào latency của mỗi request đơn lẻ.

Workload một luồng đơn (queue depth = 1) có latency thấp vì hàng gần rỗng. Nhiều luồng cạnh tranh: hàng sâu, throughput tổng tăng nhưng latency mỗi request tệ hơn. Đo throughput để hiểu sức chịu tải, đo latency để hiểu trải nghiệm mỗi request.

Q5
Bạn benchmark đọc ngẫu nhiên một file 10 GB trên SSD, lần đầu mất 30 giây, lần hai chỉ mất 3 giây. Điều gì có thể giải thích chênh lệch 10 lần đó, và cách benchmark lại cho đúng?

Lần đầu đọc từ ổ đĩa thật, lần hai đọc từ Linux page cache — dữ liệu đã nạp vào RAM và kernel giữ lại. Page cache thuộc thang nano-giây RAM, không phải micro-giây SSD, nên lần hai không đo ổ.

Fix: xoá cache trước mỗi lần đo bằng echo 3 > /proc/sys/vm/drop_caches, hoặc dùng --direct=1 trong fio để bypass hoàn toàn. Không xoá cache thì benchmark cho số đẹp hơn thực tế và bạn sẽ chọn sai thiết bị.

Bài tiếp theo: Filesystem — từ tên file tới khối dữ liệu trên đĩa

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

Đặt 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

Bài tiếp theo

Filesystem — từ tên file tới khối dữ liệu trên đĩa