Linux
Linux Storage Performance: Measure Before You Tune
Storage performance problems often produce misleading symptoms. An application may report slow queries while the real problem is a saturated device, a queue full of requests, or a workload dominated by small random I/O.
The safest tuning rule is simple: measure first, change one thing, and measure again.
Start with capacity
Before investigating performance, check whether the filesystem is simply running out of space.
df -h
For inode exhaustion:
df -i
A filesystem can have free gigabytes and still fail because it has no inodes left.
Identify the devices
lsblk gives a useful overview.
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
This helps connect an application path such as /var/lib/postgresql to the underlying block device.
Do not assume the filesystem name tells you the physical device. LVM, RAID, multipath, and virtual disks can add several layers.
Look at mount options
Use findmnt for a readable view.
findmnt /var/lib/postgresql
For all mounts:
findmnt -t xfs,ext4
Mount options can affect behavior, but changing them without understanding the workload is not a performance strategy.
Measure device activity
The classic tool is iostat, usually provided by the sysstat package.
iostat -xz 1
The -x option provides extended statistics and -z hides devices with no activity.
Watch utilization, average request size, queue depth, and latency-related fields rather than focusing on one number.
Watch CPU and I/O together
vmstat provides a quick system-wide view.
vmstat 1
The wa column represents time spent waiting for I/O. High I/O wait can be useful evidence, but it does not identify which process caused it.
For that, look at processes.
Find the process doing I/O
pidstat can show per-process I/O activity.
pidstat -d 1
This is often more useful than staring at aggregate device utilization.
If one database backend is responsible for most reads or writes, the next question is what operation that process is performing.
Use top for context
CPU saturation and storage latency can interact.
top
A single busy CPU can limit a workload even when the SSD itself is capable of much higher throughput.
This is especially important for workloads with compression, checksums, serialization, or other CPU-heavy processing around I/O.
Test the device carefully
For controlled testing, fio is the standard tool.
A simple sequential read example might look like:
fio --name=read-test --filename=/path/to/testfile --size=4G --rw=read --bs=1M --iodepth=16 --direct=1
Do not run destructive tests against a live database device.
A benchmark should use a dedicated test file or test volume and should reflect the access pattern you are actually trying to understand.
Small I/O and large I/O are different
A workload reading large sequential blocks can produce impressive MB/s numbers.
A workload doing many small random operations may care much more about latency and IOPS.
That is why “my SSD can do 7 GB/s” does not mean every database query can read at 7 GB/s.
The application decides what requests reach the device.
Check open files
When a service is active, lsof can help connect files to processes.
sudo lsof +D /var/lib/myapp
Be careful with very large directory trees because recursive scans can themselves be expensive.
For a quick process-level view:
sudo lsof -p 1234 | head
Look for kernel messages
Storage errors often appear in the kernel log.
dmesg -T | grep -Ei 'error|timeout|nvme|scsi|xfs|ext4'
On systemd systems, the journal is another useful source.
journalctl -k -b
Do not ignore repeated controller resets or filesystem warnings just because the application still works.
Filesystem choice is workload-dependent
XFS and ext4 are both mature filesystems, but they have different behavior and administration features.
The important question is not which filesystem wins a generic benchmark.
Ask what the workload does: large files, millions of small files, sequential streams, random updates, snapshots, quotas, or database storage.
Do not tune blindly
Changing I/O schedulers, read-ahead values, mount options, dirty ratios, or database settings can make one benchmark better while making the real workload worse.
Keep a record of the baseline.
uname -r
lsblk
findmnt
mount | column -t
iostat -xz 1 5
Then change one variable.
Build a repeatable test
A good storage investigation answers four questions: what is the application doing, what is the kernel doing, what is the device doing, and where is the time going?
Once those answers are visible, tuning becomes engineering instead of folklore.