为什么内存是数据库的”主战场”

数据库的核心矛盾是:数据在磁盘上,但计算必须在内存里。内存 (DRAM) 比磁盘快约 10⁵ 倍,因此”把热点数据留在内存”是几乎所有性能优化的归宿——从缓冲池到列式缓存。

DRAM 存储单元

每个比特由一对电容 + 晶体管组成:

        word line (行选通)

    ┌───────┴───────┐
    │   晶体管       │
    │    (开关)     │
    └───────┬───────┘

       ╱╲ 电容  ──── bit line (列读取)
      ╱  ╲ (电荷)
     ╱____╲
   充电=1    放电=0
  • 电容充电 → 逻辑 1,放电 → 逻辑 0
  • 晶体管像开关,行选通时连通,由列线读出电荷
  • 电容会漏电,电荷随时间衰减,因此必须定期”刷新”

刷新 (Refresh)

DRAM 必须在电容漏电前重新充电,典型刷新周期约 64 ms(每 64ms 内每行至少刷新一次)。刷新期间内存不可访问,带来微小但存在的停顿。这也是 DRAM 被称为”易失性”的原因——断电即丢数据。

行列地址与访问时序

内存按矩阵寻址:先送行地址 (RAS) 打开一行到感应放大器,再送列地址 (CAS) 选中具体字节。这对应数据库内存访问的延迟组成:

访问延迟 ≈ tRAS(行激活) + tCAS(列选中) + 传输
典型:     ~13ns + ~13ns + 突发传输

带宽与通道

内存不是单条,而是由通道 (Channel) 并联提升吞吐:

CPU ──┬── 通道0 (DDR5) ── DIMM
      ├── 通道1 (DDR5) ── DIMM
      └── 通道2 (DDR5) ── DIMM
         ↑ 双/四通道下带宽翻倍
  • 单通道 DDR5 约 30-50 GB/s,多通道叠加
  • 容量瓶颈易解,带宽瓶颈难解:多核并发扫描大表时,常先撞上内存带宽上限而非容量

NUMA 架构

多路服务器中,CPU 与内存分属不同NUMA 节点,跨节点访问更慢:

        ┌──────────┐      ┌──────────┐
        │ NUMA 0   │      │ NUMA 1   │
        │ CPU0     │      │ CPU1     │
        │ 本地内存 │─慢─│ 远程内存 │
        └──────────┘ QPI  └──────────┘
  • 本地内存访问 ~100 ns,跨节点 ~150-200 ns
  • 数据库绑核 (numactl) 让线程访问本地内存,避免跨节点抖动
  • PostgreSQL 的 shared_buffers 过大若跨 NUMA,反而因远端访问变慢

与数据库的关系

内存特性数据库应对
比磁盘快 10⁵ 倍缓冲池缓存热点页
易失 + 需刷新WAL/检查点保证持久化
64B 缓存行行/页按缓存行对齐
带宽有限控制并行扫描线程数
NUMA 远端慢绑核 + 本地内存分配

大页 (Huge Pages):默认 4KB 页表项过多,TLB 命中率低;用 2MB/1GB 大页减少 TLB miss,是 PostgreSQL/Oracle 常见优化。

参考