LinuxXXXVIII · Linux Performance Fundamentalsiostat sar
iostat and sar - storage and historical performance
What you'll learn
- Use iostat to identify storage bottlenecks
- Read await, queue depth, and utilisation
- Interpret %util correctly on serial versus parallel devices
- Use sar for historical performance data
- Configure sysstat data collection
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
iostat shows real-time disk I/O statistics. sar collects historical performance data over time. Together they give visibility into storage performance and trends.
iostat
# -x: extended statistics. Without it you get tps and kB/s only - none of
# the await, aqu-sz or %util columns this lesson is about.
# -z: skip devices with no activity, so loop devices do not fill the screen.
# 1: repeat every second. The FIRST report is an average since boot and
# tells you nothing about now; read the second and later ones.
iostat -xz 1
Output (columns elided — the real -x report is about 22 columns
wide and also carries rrqm/s, %rrqm, rareq-sz, the discard
d/* set and f/s):
Linux 6.8.0-51-generic (host) 2026-08-09 _x86_64_ (12 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
20.00 0.00 5.00 5.00 0.00 70.00
Device r/s rkB/s r_await w/s wkB/s w_await aqu-sz %util
sda 5.00 200.00 1.00 10.00 400.00 4.00 0.50 5.00
nvme0n1 100.00 10000.00 0.30 50.00 5000.00 0.50 1.50 30.00
Note that modern iostat reports r_await and w_await
separately and has no combined await column. Read them apart:
reads and writes have different service-time characteristics, and
a write-back cache can hide write latency while reads queue.
Reading:
r/s,w/s: read/write IOPS per second.rkB/s,wkB/s: read/write throughput.r_await,w_await: average time (ms) for a read, or a write, to complete — queue time included. These are the latency columns. Olderiostathad a single combinedawait; current versions report only the split form, and older documentation that refers to “await” means these.aqu-sz: the time-averaged number of requests in flight. It has no fixed saturation threshold - not 1, not any number. Read it against the device’s queue capability and alongsideawait.%util: the percentage of elapsed time during which at least one I/O request was in flight. It measures busy, not full.
aqu-sz has no threshold
Older guidance - including an earlier version of this lesson -
said aqu-sz > 1 means saturation. That is the same mistake as
reading %util as a fixed number: it turns a
capability-relative measurement into a constant.
aqu-sz follows Little’s Law, aqu-sz ≈ IOPS × await, so it
rises when the workload submits more concurrent I/O or when the
device gets slower. A SATA disk advertises NCQ depth 32; an NVMe
controller has many hardware queues, each thousands deep. On such
a device an aqu-sz of 8 or 32 can be perfectly normal
concurrency with flat latency. Conversely aqu-sz of 0.3 with an
await of 40 ms is a slow device, not a saturated one.
Saturation is the knee of the latency curve: offered load keeps
rising, IOPS stop rising, and await climbs instead. The lesson
linux-queue-depth-and-utilisation works through this in full.
Read %util the right way round
%util counts time, not work. A device with only one request
outstanding for the whole interval reads 100% just as surely as
one with a queue of 64.
On a device that serves requests serially - a single spinning
disk - one request at a time is genuinely all it can do, so 100%
does mean saturated. On a device that serves requests in
parallel - NVMe, RAID arrays, SAN LUNs, cloud block volumes -
100% only means the device was never completely idle. An NVMe
drive can sit at %util 100 while delivering 2% of the IOPS it
is capable of.
So the widely repeated rule “on SSDs, %util reads low even at
high IOPS” is backwards. Parallel devices read high, and
early. man iostat says so directly:
%util Percentage of elapsed time during which I/O requests were
issued to the device (bandwidth utilization for the
device). Device saturation occurs when this value is close
to 100% for devices serving requests serially. But for
devices serving requests in parallel, such as RAID arrays
and modern SSDs, this number does not reflect their
performance limits.
On a parallel device, ignore %util and judge saturation from
await (and latency percentiles if you have them) against
IOPS compared with the device’s known rated capability.
Identify the bottleneck
iostat -xz 1
-x shows extended stats; -z hides idle devices. Look for:
- High
r_await/w_await(>10ms typical for spinning disks,1ms for SSDs). Current
iostatreports the two directions separately and has no combinedawaitcolumn - read them apart, because a write-back cache can keepw_awaitlow while reads queue behind it. aqu-szhigh relative to that device’s queue capability, and rising whileawaitrises with it.aqu-szalone, against any constant, proves nothing.- Errors in the device.
For NVMe, also check:
nvme smart-log /dev/nvme0n1
SMART data shows media errors, temperature, wear.
sar
# CPU history
sar -u 1 5 # 5 samples at 1-second interval
# Memory history
sar -r 1 5
# Disk history
sar -d 1 5
# Network history
sar -n DEV 1 5
# Load average history
sar -q 1 5
sar reads from /var/log/sa/ (or /var/log/sysstat/).
Configure sysstat data collection
# /etc/default/sysstat
ENABLED="true"
The sa1 and sa2 cron jobs collect data every 10
minutes. Historical data is kept for 28 days by default.
# View historical CPU usage
sar -u -f /var/log/sa/sa15 -s 14:00 -e 16:00
# Compare current to yesterday
sar -u -s 14:00 -e 16:00
Common patterns
| Pattern | Cause |
|---|---|
| High await (>10ms) | Slow disk, I/O queue saturation |
| High %util + high await | Disk is the bottleneck, on any device type |
| High %util + low await | Device is busy but coping. On a parallel device (NVMe, RAID, cloud volume) this is normal and not a finding |
| aqu-sz growing and await growing with it | Real queueing. Compare against that device’s queue capability, never against a fixed number |
| aqu-sz growing, await flat | More concurrency, not more pressure. Not a finding |
| Many small I/Os | Application doing random reads/writes |
| Many large I/Os | Backup or batch job running |
Trends over time
sar lets you compare current vs. historical:
# What was the disk I/O at 14:00 yesterday?
sar -d -f /var/log/sa/sa15 -s 14:00 -e 15:00
# What is it today?
sar -d -s 14:00 -e 15:00
A sudden change (e.g. disk I/O doubled) is a signal of something new running or a problem.
Knowledge check
Knowledge check · 5 questions
Q1. What do the r_await and w_await columns in `iostat -x` measure?
Q2. sar data is collected by default on most Linux distributions.
Q3. Which of the following are valid sar options? Select all that apply.
Q4. An NVMe device can report %util near 100 while running at a few percent of its rated IOPS.
Q5. An application is slow. iostat -x 1 on the NVMe data volume shows %util 99, await 0.4 ms, r/s 900 on a drive rated for well over 100k IOPS. What do you conclude?
Passing score: 75%. Answers are checked in this browser.