⚙️ 第五章 内存与磁盘的密切联系
📄 一句话概括

磁盘缓存=把读过的数据在内存里留一份;它靠的是局部性——程序访问数据往往扎堆在附近几块上。

📝 本节和前面几节的关系

5.1 摆出了内存与磁盘的速度差,这一节给第一条路:少去磁盘。

一、办法很朴素:留一份

既然去一次磁盘那么贵,那就去过就别白去。 磁盘缓存的做法是:读的时候顺手在内存里留一份副本;下次还要同一块,直接从内存给。 写也一样——先写进缓存,攒一攒再一起落盘,把很多次小写合成一次大写。 所以缓存既是【加速】,也是【合并】。

二、为什么它一定有用:局部性

凭经验我们相信缓存能命中,但凭什么是必然的?靠的是【局部性】。 时间局部性:刚用过的数据,很可能马上还要再用(比如循环里的计数器)。 空间局部性:用了某一格,很可能接着用旁边几格(比如遍历数组)。 程序的行为天然带这两种偏爱——所以缓存只要猜中这一点,就能拦下大部分访问。

三、命中率才是关键

缓存有用,不代表可以乱缓存。真正决定效果的是命中率。 命中率高——绝大部分访问都在内存里解决,磁盘几乎不被打扰。 命中率低——每次都白缓存一次,反而多花了管理缓存的开销。 而缓存容量有限,只能装一部分,所以【留下什么、赶走什么】是它最要紧的策略。

四、这一招到处都在用

CPU 有缓存,磁盘有缓存,操作系统、数据库、浏览器、CDN 全都各有一层缓存。 它们的共同点一模一样:慢的东西前面,放一小块快的东西挡着。 所以你一旦看懂了磁盘缓存,几乎就同时看懂了其他所有缓存。 要小心的一点也相同:缓存里的数据可能不是最新的——这是所有缓存都得面对的代价。

五、动手 Demo:提高磁盘访问速度的磁盘缓存

磁盘缓存的做法很朴素:把读过的数据在内存里留一份。 下次再要同一块,直接从内存给,不用去碰磁盘。

cache = {}
hits = 0
for a in (7, 3, 7, 3, 9, 7, 3, 7, 9, 3):
    if a in cache:
        hits += 1
        print("块 %d 命中" % a)
    else:
        cache[a] = True
        print("块 %d 未命中,去磁盘读" % a)
print("命中 %d 次" % hits)

实跑输出:

--------------------------------------------------------------------
提高磁盘访问速度的磁盘缓存
--------------------------------------------------------------------

磁盘缓存的做法很朴素:**把读过的数据在内存里留一份。**
下次再要同一块,直接从内存给,不用去碰磁盘。

  模拟一段地址序列,看留一份能省下多少次磁盘访问:

  第几次     要哪一块     结果                     累计命中
  ----------------------------------------------------------
  1          7            未命中,去磁盘读进来     0
  2          3            未命中,去磁盘读进来     0
  3          7            命中,直接从内存给       1
  4          3            命中,直接从内存给       2
  5          9            未命中,去磁盘读进来     2
  6          7            命中,直接从内存给       3
  7          3            命中,直接从内存给       4
  8          7            命中,直接从内存给       5
  9          9            命中,直接从内存给       6
  10         3            命中,直接从内存给       7

  10 次访问命中 7 次,磁盘只被碰了 3 次。

  **这就是【局部性】的价值**:程序的访问往往扎堆,
  同一块数据被反复用——缓存只要猜中这一点,
  就能把大部分访问拦在内存里。

10 次访问命中 7 次,磁盘只被碰了 3 次——因为同一块被反复要。 这就是局部性的价值:程序的访问扎堆,缓存才拦得住。

30 秒实验

  1. 把访问序列改成 1、2、3、4、5、6、7、8、9、10,再看命中率会变成多少。
  2. 想想浏览器的缓存和这里说的磁盘缓存,是不是同一个思路。

常见误区

  1. 以为缓存越大越有用。装不下的部分才是关键,命中率比容量重要。
  2. 以为缓存里的数据总是最新的。多一份副本就多一份不一致的风险。
自测三问(点开对答案) 1. 磁盘缓存的基本做法是什么? —— 把读过的数据在内存里留一份副本,下次要同一块就直接从内存给。 2. 缓存为什么通常能命中? —— 因为程序的访问有局部性:刚用过的、旁边的数据很快还会被用到。 3. 缓存是免费的吗? —— 不是。容量有限、要管理换进换出,还可能造成缓存与实际数据不一致。

相关笔记