CephII · Storage Performance FundamentalsStorage Performance Fundamentals
Queue depth and block size — the two knobs that set the cost
What you'll learn
- Explain what queue depth and block size each control in the I/O path
- Predict how a change in either affects IOPS, throughput, and latency
- Choose benchmark parameters that represent a real workload
- Identify the Ceph settings that expose these parameters to the operator
Prerequisites
None — start here.
Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18
Why this matters in production
Two benchmarks of the same cluster can differ by a factor of fifty and both be correct. The difference is nearly always queue depth and block size, and an operator who cannot reason about them cannot tell a meaningful measurement from a marketing one — or design a test that resembles the workload they actually run.
Queue depth: how much is in flight
Queue depth is the number of I/O requests submitted but not yet completed. It is a property of the client, not the device.
- Queue depth 1 is a synchronous workload: submit, wait, submit.
A database committing a transaction,
fsync()on a log file, a single-threaded copy. Latency is the entire story here — the device is idle between requests. - Queue depth 32 or 128 is a concurrent workload: many threads or an async engine keeping the device busy. Throughput and IOPS look much better; individual operations take longer.
The device’s own internal parallelism decides how much this helps. A spinning disk has one head and gains little beyond a small depth. A SATA SSD has a queue of 32. An NVMe drive has many queues of great depth and scales until something else is the bottleneck.
Block size: how much per request
Block size is how many bytes one request carries. Small blocks make the per-operation overhead dominant; large blocks amortise it.
4 KiB database pages, metadata, small random I/O
64 KiB filesystem readahead, mixed workloads
1-4 MiB backups, recovery, sequential streaming
4 MiB Ceph's default RADOS object size for RBD
The last line matters. RBD stripes an image into 4 MiB objects by default, so a 4 KiB write from a VM becomes a partial write to one 4 MiB RADOS object, and a 16 MiB sequential write touches four objects that may live on entirely different OSDs — which is exactly how Ceph gets parallelism on large transfers.
Where Ceph exposes these
# RBD stripe geometry, set at image creation
rbd create pool/image --size 100G --object-size 4M \
--stripe-unit 4M --stripe-count 1
rbd info pool/image # shows object size and striping
For most workloads the defaults are right. Smaller objects spread a single image across more PGs, which raises parallelism and also raises the number of objects the cluster tracks. Larger objects reduce metadata and concentrate a client’s I/O on fewer OSDs.
On the OSD side, the queue is governed by settings such as
osd_op_num_threads_per_shard and osd_op_num_shards, and by the
recovery throttles that decide how much of the device’s queue
recovery is allowed to occupy. Those are covered in the recovery
tuning part; the relevant point here is that recovery and client I/O
compete for the same finite queue.
Reading the two together
The useful mental model is a two-by-two:
| Small block | Large block | |
|---|---|---|
| Low queue depth | latency-bound; databases | rare; single-stream copy |
| High queue depth | IOPS-bound; VM fleets | throughput-bound; recovery, backup |
Each quadrant has a different bottleneck and a different fix. Knowing which quadrant a workload sits in tells you whether to look at device latency, at parallelism, or at network bandwidth.
Quiz
Knowledge check · 4 questions
Q1. A colleague reports that the cluster achieves 2.2 GiB/s in their benchmark, but the PostgreSQL server on the same pool commits at 9 ms. Which detail of the benchmark most likely explains the difference?
Q2. On an erasure-coded pool, a small write that does not cover a full stripe requires reading other chunks before it can be written.
Q3. A team plans to move a VM fleet from a replicated pool to an erasure-coded pool to reclaim capacity, citing a benchmark showing the EC pool at 1.8 GiB/s. Assess the plan.
Existing: 3-way replicated pool, 40 TiB used, 60 VMs, mixed Linux and Windows guests. Proposed: k=4 m=2 erasure-coded pool, which would reduce raw usage from 120 TiB to 60 TiB. Benchmark cited: rados bench with 4 MiB objects, showing 1.8 GiB/s write. VM workload profile has not been measured.
Q4. Describe the four quadrants of block size against queue depth, and name a workload that sits in each.
Passing score: 75%. Answers are checked in this browser.
Production discipline
Record block size and queue depth with every performance number you produce or receive; a figure without them is not comparable to anything. Benchmark the quadrant the workload actually occupies, not the one the hardware is best at. And carry the erasure-coding consequence into design conversations: partial-stripe writes are a read-modify-write across k+m OSDs, which makes EC excellent for large sequential objects and unsuitable for small random writes, whatever a 4 MiB benchmark says.
Cross-course references
- Linux: Part XLI (Disk Performance) for fio and the block layer queue.
- Ceph: Part XXV (Erasure Coding Fundamentals) develops the stripe-write cost in detail.
- Ceph: Part LX (Recovery Tuning) covers the queue that recovery and clients share.