Skip to content

The database never crashed. Why did swapping make every query slow?

The Machine Had More Resources. Why Was It Slower? · Part 5 of 6 · 6 min read

Read the worked explanation ↓

Interview question. A database node has 96 GB of RAM. Query p99 climbs from 45 ms to 4 seconds after an analytics job starts. The database stays alive, CPU usage falls, disk latency rises, and the host begins swapping. Explain what memory pressure is doing, identify evidence that distinguishes page faults from disk saturation, and design a safe memory budget.

The kernel may spend increasing amounts of time trying to make space for new allocations. Its process responds occasionally, health checks may still pass. But much of the useful work has stopped because threads are waiting for memory pages to become available.

Consider the database's buffer cache: 60 GB of hot index and table pages, plus 12 GB for worker memory, connections and runtime overhead. The operating system also holds file-cache pages, metadata and other services. Now a colocated job allocates 35 GB. The arithmetic is not simply 60 + 12 + 35 versus 96, because some pages can be reclaimed. But the working sets are now competing for RAM.

Under memory pressure, a database can alternate between reclaiming file-cache pages and faulting swapped anonymous pages back in, inflating query latency.
Under memory pressure, a database can alternate between reclaiming file-cache pages and faulting swapped anonymous pages back in, inflating query latency.
Think first

A database uses 60 GB of hot pages, runs on a 96 GB host, and a new analytic job begins allocating 35 GB. The host reports swap use, page faults and low CPU. Describe the evidence needed to prove memory thrashing, your first three mitigations, and a longer-term admission-control policy that still allows bounded analytics.

Take a few minutes to form an approach, then compare it with the worked explanation.

Reveal the worked explanation

Why reclaim can be worse than an immediate failure

Linux distinguishes several broad classes of resident pages. Clean file-cache pages can often be reclaimed and read again from the backing file later. Dirty file pages may need writeback first. Anonymous memory, such as portions of heaps and in-memory hash tables, may be swapped if swap is configured and policy permits it. Some pages are unevictable or require special handling.

The kernel may scan memory to find reclaimable pages, evict cache and write some pages out. If the database touches an evicted file page, it must read it again. If a thread touches an anonymous page that was swapped out, it incurs a swap-in fault. Either can add I/O to a request that previously completed entirely from memory.

A database that repeatedly scans a working set larger than the available RAM can churn: it loads pages just evicted to make room for other pages it will soon need. That is different from healthy file-cache recycling. Reclaim activity can consume CPU and saturate storage, while application CPU looks unexpectedly low because its threads wait on page faults or I/O.

No single metric proves this. A large value for SwapUsed can represent pages swapped out long ago and never touched again. What matters for the incident is active swap-in and swap-out traffic, major page faults, reclaim stalls, and the overlap with query latency. Occupied swap can coexist with good performance if no hot pages are faulted back in.

An incident timeline I would reconstruct

Start with when latency changed. Compare available memory, resident and virtual size, page cache, swap I/O rate, major faults, reclaim scans, pgscan and pgsteal where available, and storage latency. On modern Linux, pressure stall information from /proc/pressure/memory helps quantify how much time tasks lose waiting on memory. Under cgroup v2, memory.pressure, memory.events, memory.current and memory.swap.current can localize problems to containers.

I would use vmstat carefully: sustained si and so activity is more meaningful than a single swap-used number. Check disk read and write rates together with queue latency. If storage is saturated by an unrelated backup, high fault latency may be a symptom of I/O competition rather than memory alone. Compare actual working-set growth to the start time of the analytics job.

Inspect the database's own memory controls. A configured 60 GB buffer pool does not mean the process consumes exactly 60 GB. Sort and hash workspaces, query parallelism, connections, allocators and mapping overhead add pressure. For a memory-mapped engine, file cache and process RSS need careful interpretation because metrics can overlap. Avoid adding free fields together as if they were independent allocations.

The first production decision

During an active incident, I would stop or rate-limit the colocated analytics workload if its resource contract allows it. That might free memory and I/O capacity faster and more safely than restarting the database. A restart can temporarily free allocations but also destroys warmed caches and may produce a new thundering herd when traffic reconnects. If a database process has genuinely leaked memory, a controlled restart may still be required, but use evidence rather than habit.

Do not set swap to zero universally as the first step. Disabling swap can prevent some latency failure modes but may turn a survivable pressure condition into an OOM kill, depending on kernel and cgroup configuration. The right policy depends on how the service handles memory exhaustion, whether it can shed load, and how costly an abrupt termination is. Latency-sensitive databases often reserve headroom and strongly control memory overcommit, but the safety tradeoff must be explicit.

Design the budget, not just the limit

I would reserve capacity for the database's working set, variable query memory, the operating system, page-cache needs, networking, and recovery tasks. Put analytics in a separate resource domain or place enforceable memory and I/O limits around it. For cgroups, memory.high can apply reclaim pressure before hitting a hard memory.max, while memory.max limits usage and may lead to cgroup OOM if reclaim cannot make sufficient room. Their behavior must be tested against the database's fault model.

Add overload protection before memory is exhausted. Limit concurrent memory-heavy queries, bound per-query workspace allocation, and admit fewer background scans when memory pressure rises. Keep dashboards for memory PSI, major faults, swap I/O and p99 query classes, not only percent-used RAM.

The deeper interview point is that a process can keep running while the machine loses forward progress. Availability that reports only process liveness misses that distinction. Your capacity design should protect the resident working set needed for the latency promise, not just prevent the operating system from killing a PID.

Go deeper

Linux PSI some and full distinguish partial versus all-nonidle-task stalls for memory and I/O, subject to the scope and measurement. Cgroup v2 exposes memory pressure and events, a soft throttle/reclaim boundary through memory.high, and a hard limit through memory.max. Swap behavior is influenced by system configuration, cgroup swap controls and reclaim choices. A major page fault means an access needed I/O, but it should be correlated with the workload's actual swap and file-cache activity. memory.current, RSS, virtual memory size and page-cache metrics have different accounting semantics.

Check your reasoning

  • Differentiate evicted file-backed pages from swapped anonymous pages.
  • Use active major faults, swap I/O, PSI and storage latency to establish thrashing.
  • Avoid confusing swap occupancy or RSS with active memory pressure.
  • Choose immediate workload shedding versus dangerous blind database restarts.
  • Define memory budget, query concurrency and cgroup failure behavior.

Common mistake: If swap is occupied, swapping must be causing current p99 latency.

A live database slows after a colocated analytics job grows. Which investigation establishes whether memory pressure is responsible?

Read the answer explanation

Correlate page faults, active swap I/O, PSI and query delays before mitigation — Swap occupancy alone does not show hot pages being faulted back in now.

Apply it elsewhere

A vector-search node with a memory-mapped index develops 3-second tails after a deployment changes index size. Propose a memory and query-concurrency budget that accounts for page cache without double-counting it as independent application heap memory.

Compare your approach

Differentiate anonymous and file-backed pages, active swap activity and cached swap occupancy, inspect PSI and major faults, avoid ambiguous memory accounting, and defend a failure policy for memory limits and overload.

One more constraint

Does 10 GB of swap occupancy by itself prove that active swapping is causing current database latency?

Quick revision

Memory pressure can make a database alive but unusably slow. Reclaim evicts file-cache pages and may swap anonymous pages, so hot data must be faulted in again. Diagnose active swap I/O, major faults, reclaim stalls, PSI, storage latency and query-class p99 rather than swap occupancy alone. Stop competing workloads safely, protect the resident working set, bound memory-heavy query concurrency and reserve headroom. A strict memory or no-swap policy changes OOM risk, so test the failure mode rather than applying it blindly.

References