Why memory is the database’s “main battlefield”

The core tension of databases: data lives on disk, but computation must happen in memory. Memory (DRAM) is about 10⁵× faster than disk, so “keeping hot data in memory” is the destination of nearly every performance optimization — from buffer pools to columnar caches.

The DRAM storage cell

Each bit is a pair of capacitor + transistor:

        word line (row select)

    ┌───────┴───────┐
    │    transistor  │
    │     (switch)   │
    └───────┬───────┘

       ╱╲  capacitor ──── bit line (column read)
      ╱  ╲ (charge)
     ╱____╲
   charged=1   discharged=0
  • Capacitor charged → logic 1, discharged → logic 0
  • The transistor acts as a switch; when the row is selected it connects, and the column line reads the charge
  • Capacitors leak: charge decays over time, so the cell must be periodically “refreshed”

Refresh

DRAM must recharge cells before they leak away. A typical refresh period is about 64 ms (each row refreshed at least once within 64 ms). During refresh the memory is inaccessible, causing small but real stalls. This is also why DRAM is called “volatile” — power loss means data loss.

Row/column addressing and access timing

Memory is addressed as a matrix: first send the row address (RAS) to open a row into the sense amplifiers, then send the column address (CAS) to select the specific byte. This maps to the latency composition of database memory access:

Access latency ≈ tRAS (row activate) + tCAS (column select) + transfer
Typical:        ~13ns + ~13ns + burst transfer

Bandwidth and channels

Memory is not a single stick; it is parallelized by channels to raise throughput:

CPU ──┬── Channel 0 (DDR5) ── DIMM
      ├── Channel 1 (DDR5) ── DIMM
      └── Channel 2 (DDR5) ── DIMM
         ↑ dual/quad-channel doubles bandwidth
  • A single DDR5 channel is ~30-50 GB/s, multiplied across channels
  • Capacity is easy to scale, bandwidth is hard: scanning a large table with many cores often hits the memory bandwidth ceiling first

NUMA architecture

In multi-socket servers, CPUs and memory belong to different NUMA nodes, and cross-node access is slower:

        ┌──────────┐      ┌──────────┐
        │ NUMA 0   │      │ NUMA 1   │
        │ CPU0     │      │ CPU1     │
        │ local mem│─slow─│ remote mem│
        └──────────┘ QPI  └──────────┘
  • Local memory access ~100 ns, cross-node ~150-200 ns
  • Binding threads to cores (numactl) lets them use local memory, avoiding cross-node jitter
  • If PostgreSQL’s shared_buffers is too large and crosses NUMA, remote access can actually slow things down

Relationship to databases

Memory traitDatabase response
10⁵× faster than diskbuffer pool caches hot pages
volatile + needs refreshWAL / checkpoint for durability
64B cache linerows/pages aligned to cache line
limited bandwidthcap parallel scan thread count
NUMA remote is slowcore binding + local allocation

Huge Pages: the default 4KB page tables have too many entries and low TLB hit rate; using 2MB/1GB huge pages reduces TLB misses — a common optimization for PostgreSQL/Oracle.

References