我见过最吓人的一次运维事故,是我同事把MySQL的数据目录建在了/tmp下。重启的那一瞬间,所有表全没了,他一脸茫然地问我:为什么重启就丢了?我问他你听说过tmpfs吗?他说知道啊,就是缓存嘛。那一刻我才意识到,很多人对存储机制的理解,可能只停留在"SSD比HDD快"这个层面。
存储机制这四个字,听起来像是底层领域的专属名词,但几乎你写的每一行代码都在跟它打交道。一条SQL能跑多快,一个KV写入要多久,一次扩容该买多少块盘,甚至一场宕机后数据能不能救回来,都取决于你对存储的理解。这篇文章从单机缓存讲到多机分布式,从机械时代的RAID讲到QLC和ZNS,把存储机制这棵树的根和梢都捋一遍,希望能帮你建立一套完整的存储认知地图。
1. 存储分层的底层逻辑:CPU为什么不愿意直接访问硬盘
1.1 速度差距大到你无法想象
很多人以为存储分层是为了省钱,这只是表面原因。真正的原因是:CPU和存储设备之间的性能鸿沟,已经大到不分层就根本没法用的程度。
拿最直观的数据来对比。CPU的L1缓存访问延迟大约0.5纳秒,主内存大约80纳秒,一块企业级NVMe SSD延迟在100微秒左右,而一块7200转的SATA机械硬盘,随机访问延迟大约是7毫秒。这个差距有多恐怖?如果L1缓存的1秒对应现实中我们感知的1秒,那么内存访问就是160秒,SSD大约是23小时,而机械硬盘整整需要162天。
这还只是延迟维度。我们再来算带宽。一条PCIe 5.0 x4的NVMe硬盘顺序读能跑到14GB/s,看起来已经很快了对吧?但主内存的带宽是几百GB/s,L2缓存的带宽高达TB/s级别。而且顺序读还好,一旦碰到随机IO,机械硬盘的IOPS可能只有两三百,SSD能做到几十万甚至上百万IOPS,内存则是百万到千万级。
这就是为什么CPU宁可把大量空间花在三级缓存上,也不愿意直接访问外存。每一层的存在,都是在给上一层"兜底",用尽量小的代价,掩盖下一层让人绝望的延迟。
1.2 局部性原理:缓存能成立的根本原因
但问题来了:就算缓存再快,如果程序每次访问的数据都不在缓存里,缓存也形同虚设。存储分层能成立,靠的是一个非常有意思的经验法则——局部性原理。
简单说就是两句话:时间局部性——刚访问过的数据,短时间内很可能再次被访问;空间局部性——刚访问过地址附近的数据,很可能接下来也会被访问到。操作系统运行指令是在顺序取指,数组遍历是在连续扫描,循环体在反复执行同一段代码,这些都天然符合局部性。
举一个很生活化的例子。你在家里做饭,调料、锅具放在手边(L1缓存),冰箱里放着洗好的菜(L2缓存),楼下超市是日常补给(内存),而城外的大仓库才是真正储备所有物资的地方(磁盘/对象存储)。你不会把仓库里的所有东西都搬到厨房,只会把这一顿饭要用到的备好。存储分层也是这个逻辑,数据越热,越往上层放。
不过这里有个很关键的细节,缓存的替换策略决定了命中率。LRU(最近最少使用)是大家最熟悉的策略,但实际工程里几乎没人用干净的LRU,因为标准LRU对一次扫描型读负载非常不友好,会直接把整个缓存污染掉。Linux内核的页缓存用了一种改进的LRU算法,把页面分成活跃链表和不活跃链表,新页面先进不活跃链表,第二次被访问才升级到活跃链表。这样即使一次读一个大文件,也不会把热数据全部挤出去。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一次写入请求在单机内部走完的完整路径
2.1 CPU缓存里的写入:先改内存还是先改缓存
先看一个平时几乎不会注意到的层面:CPU缓存和内存之间的写入一致性。这比你想象的要复杂。
当CPU执行一条写入指令时,数据并不是直接写到内存的,而是先写进L1缓存。这时候有两种策略。一种是write-through,也就是写缓存的同时立刻写内存,实现简单,但每次写入都要等慢速内存,性能差。另一种是write-back,先只改缓存,把这个缓存行标记成"脏"的,等到它被换出缓存的时候才真正写回内存。现代CPU几乎都采用write-back,因为绝大多数写入的目标地址
