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_buffersis too large and crosses NUMA, remote access can actually slow things down
Relationship to databases
| Memory trait | Database response |
|---|---|
| 10⁵× faster than disk | buffer pool caches hot pages |
| volatile + needs refresh | WAL / checkpoint for durability |
| 64B cache line | rows/pages aligned to cache line |
| limited bandwidth | cap parallel scan thread count |
| NUMA remote is slow | core 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
- DDR5 SDRAM Standard, JEDEC
- NUMA Best Practices, Red Hat
- PostgreSQL: Huge Pages