第 14 章

存储金字塔

存储金字塔

快的贵,慢的便宜——这是存储世界万古不变的铁律。如果有一种存储又快又便宜又大,其他所有存储都会立刻被淘汰。现实是:物理约束决定了快速存储必须昂贵且容量小,廉价存储必然缓慢。整个计算机的存储体系,就是在这个约束下设计出来的一座金字塔

Level 1:建立直觉

存储金字塔的全貌

从上到下,速度递减,容量递增,成本递减:

              ▲
              █  CPU 寄存器
              █  (几十个,亚纳秒,造价极高)
              █
             ███  L1/L2 缓存
             ███  (KB-MB,1-12 周期,SRAM,芯片内置)
             ███
            █████  L3 缓存
            █████  (MB-数十MB,40周期,多核共享)
            █████
           ███████  主内存(RAM)
           ███████  (GB,~150周期,DRAM,断电丢失)
           ███████
          █████████  NVMe SSD
          █████████  (TB,微秒级,断电保留)
          █████████
         ███████████  SATA SSD / HDD
         ███████████  (TB,毫秒级,断电保留)
         ███████████
        █████████████  网络存储 / 云存储
        █████████████  (PB,10ms-100ms,断电保留)
        █████████████
       ███████████████  冷存储(磁带、光盘)
       ███████████████  (EB,秒到分钟,超低成本)

每层的关键数字(2024年数据):

层级          容量范围    访问延迟    带宽           成本/GB
寄存器        ~1KB        0.3ns       -              不可买
L1 缓存       32-192KB    1ns         ~3 TB/s        ~$100/GB(芯片成本)
L2 缓存       256KB-16MB  3ns         ~1 TB/s        ~$10/GB
L3 缓存       8-64MB      10ns        ~500 GB/s      ~$1/GB
主内存 DDR5   8-128GB     50ns        100-273 GB/s   ~$5/GB
NVMe SSD      512GB-4TB   0.1ms       7 GB/s         ~$0.08/GB
SATA SSD      512GB-4TB   0.5ms       500 MB/s       ~$0.06/GB
HDD           1-20TB      10ms        200 MB/s       ~$0.02/GB
云存储        无限        10-100ms    视网络          ~$0.02/GB/月
磁带(归档)  数十TB/卷   秒-分钟     几百 MB/s       ~$0.003/GB

为什么需要这座金字塔

直觉思考:如果有无限的 L1 缓存,我们就不需要内存了。如果有无限的内存,我们就不需要 SSD 了。为什么不这样做?

因为 SRAM(L1/L2 使用的存储技术)太贵了

一个 SRAM 单元需要 6 个晶体管(6T-SRAM),稳定、快速但面积大;一个 DRAM 单元只需要 1 个晶体管 + 1 个电容(1T1C),便宜但需要刷新。SRAM 的成本约为 DRAM 的 100 倍/GB。

如果把 Apple M4 Pro 的全部 24GB RAM 换成 SRAM:

物理上不可能在一块芯片上集成那么多 SRAM。

金字塔是妥协的艺术:用少量极快的存储减少对慢速大容量存储的需求,整体性能接近最快的存储,成本接近最便宜的存储。

局部性原理:金字塔运作的基础

金字塔能够奏效,核心假设是:程序在时间和空间上访问内存具有局部性

如果程序每次都随机访问不同的内存地址,缓存命中率接近 0%,金字塔完全失效。

幸运的是,现实中几乎所有程序都表现出强局部性:

典型程序的缓存命中率:

Level 2:原理剖析

内存墙:冯·诺依曼瓶颈

1977 年,沃尔夫-冯-诺依曼架构(处理器和内存分离)被指出存在一个基本瓶颈:处理器的速度增长远快于内存带宽和延迟的改善

这个差距叫做"内存墙(Memory Wall)":

1986-2000年速度提升:
CPU 性能:每年提升 ~55%
内存延迟:每年改善 ~7%

结果:CPU 和内存之间的速度差距从 1倍 扩大到 100倍

缓存金字塔就是应对内存墙的工程方案。

但即使有了缓存,现代 AI 计算已经开始突破金字塔的限制:

磁带:最后的底层

磁带听起来是上古技术,但 2024 年仍是大规模冷数据存储的主力:

Google、Facebook(Meta)、Amazon AWS 等都在大规模使用磁带:

有意思的是,磁带的顺序读取速度(约 400 MB/s)并不慢,只是随机访问需要"快进倒带"——最坏情况下要花几分钟。

I/O 延迟的完整视图

让我们把所有层级的延迟用"人类时间"来感知(假设 CPU 一个时钟周期 = 1 秒):

1 CPU 周期 = 1 秒(人类时间尺度)

L1 缓存:          4 秒
L2 缓存:          12 秒
L3 缓存:          40 秒
主内存(DDR5):   约 2 分钟
NVMe SSD:         约 2 天
SATA SSD:         约 10 天
机械硬盘:         约 300 天(近1年!)
网络(局域网):   约 300 天到3年
网络(互联网):   约 3-30 年

当 CPU 等待一次 HDD 随机访问时,从它的视角来看,就像等了整整一年什么都没干。这就是为什么磁盘 I/O 如此昂贵。

预取和提前加载:让金字塔更聪明

金字塔的关键是:上层存储要提前把下层数据加载进来,而不是等到 CPU 要用了才开始。

硬件预取器(Hardware Prefetcher)

操作系统的页面预取

数据库的缓冲池(Buffer Pool)

Level 3 · 规范怎么定义的(资深)

存储层级的理论基础

存储金字塔的理论基础是 局部性原理(Principle of Locality),由 Peter Denning 在 1968 年的"工作集模型"(Working Set Model)论文中形式化定义。工作集定义为在过去 τ 个时间单位内被访问过的页面集合 W(t,τ),只要物理内存能容纳当前工作集,程序就不会频繁发生缺页(thrashing)。

存储层级的性能模型使用 AMAT(Average Memory Access Time) 公式:AMAT = Hit Time + Miss Rate × Miss Penalty。这个公式递归适用于每一层:L1 Cache 的 Miss Penalty 就是 L2 的 AMAT,L2 的 Miss Penalty 就是 L3 的 AMAT,以此类推。整个金字塔的设计目标是让 AMAT 接近最快层(L1)的延迟,同时容量接近最慢层(磁盘/网络存储)的总和。

内存墙(Memory Wall)问题由 Wulf 和 McKee 在 1995 年的论文 "Hitting the Memory Wall: Implications of the Obvious" 中首次提出。他们指出 CPU 速度每年提升约 60%,而 DRAM 延迟每年只改善约 7%,差距以每年 50% 的速度扩大。这一趋势推动了 Cache 容量的持续增长(现代 CPU 的 L3 Cache 已达 100MB 以上)和各种预取技术的发展。CXL(Compute Express Link,基于 PCIe 5.0/6.0)协议的出现,目标正是通过扩展内存层级来缓解内存墙问题。

Level 4 · 边界与陷阱(所有人)

陷阱 1:忽视存储层级导致性能差异达 1000 倍

同样是"读取 4KB 数据",从 L1 Cache 读取约 1ns,从 L2 约 5ns,从 L3 约 20ns,从主存约 100ns,从 NVMe SSD 约 10μs,从 HDD 约 10ms。从 L1 到 HDD 的延迟差距是 1000 万倍。一个数据库查询如果能命中 buffer pool(内存),延迟在微秒级;如果需要读磁盘,延迟在毫秒级——用户体验天差地别。MySQL 的 innodb_buffer_pool_size 设置得太小是最常见的数据库性能问题之一,本质上就是因为没有利用好存储金字塔。

陷阱 2:预取(Prefetch)过于激进会适得其反

硬件预取器会检测顺序或规律的内存访问模式,提前将数据加载到 Cache。但如果预取了不需要的数据,会驱逐 Cache 中真正有用的数据(Cache Pollution)。当程序同时有顺序扫描和随机访问两种模式时(如数据库的全表扫描 + 索引查找),预取器对扫描模式很有效,但预取来的数据会把索引页挤出 Cache。Linux 的 madvise(MADV_SEQUENTIAL)MADV_RANDOM 正是让程序告诉内核"我的访问模式是什么",以调整预取策略。PostgreSQL 在顺序扫描时会使用"缓冲区环"(buffer ring)策略,限制扫描只使用一小部分 buffer pool,避免污染全局缓存。

陷阱 3:Swap 不是"免费的内存扩展"

当物理内存不足时,操作系统会将不活跃的页面换出到 Swap 空间(通常在 SSD 或 HDD 上)。理论上这让程序可以使用超出物理内存的地址空间,但实际上 Swap 的速度比内存慢 100-10000 倍。一旦程序频繁触发 Swap(thrashing),系统会变得极其缓慢——CPU 大部分时间在等待磁盘 I/O 而非计算。在生产环境中,Redis 和数据库通常建议 禁用 Swap 或设置 vm.swappiness=0,宁可 OOM(Out of Memory)Kill 也不要让服务在 Swap 中苟延残喘。

本章评分
4.6  / 5  (20 评分)

💬 留言讨论