简单结论:
1, Buffer不是缓存,国内常用的翻译是缓冲区。
2,其次,大部分场景中,Buffer是特指内存中临时存放的IO设备数据——包括读取和写入;而Cache的用处很多——很多IO设备(例如硬盘、RAID卡)上都有Cache,CPU内部也有Cache,浏览器也有Cache。
3,Buffer并非用于提高性能,而Cache的目的则是提高性能。
4,涉及到IO设备读写的场景中,Cache的一部分本身就是Buffer的一种。如果说某些场合Buffer可以提升IO设备的读写性能,只不过是因为Buffer本身是Cache系统的一部分,性能提升来自于Cache机制。
5,Buffer占用的内存不能回收,如果被强行回收会出现IO错误。Cache占用的内存,除实现Buffer的部分外都可以回收,代价则是下一次读取需要从数据的原始位置(通常是性能更低的设备)读取。
6,在IO读写过程中,任何数据的读写都必然会产生Buffer,但根据Cache算法,可能会有相当部分数据不会被Cache。
背景知识一:我们现在的计算机、手机都是冯诺依曼架构,CPU只能操作内存中的数据,无法直接操作硬盘上的数据。更多关于现代计算机的架构,有兴趣的可以看我的专栏文章:
背景知识二:硬盘上的数据,最小读写单位是扇区(Sector)。老式硬盘上一个扇区是512字节,现代硬盘上一个扇区是4K字节。计算机不能以单个字节为单位访问硬盘上的数据。现在很常见的固态硬盘,物理上最小读写单位是页(Page),但大部分固态硬盘通过主控芯片模拟传统硬盘的扇区来进行读写。现代硬盘常用的LBA(Logical Block Addressing,逻辑块寻址)寻址方式,是把硬盘上的扇区分配从0~N-1的编号(N为硬盘上所有可用扇区数量)。
介绍完背景,假设某个应用现在需要读取一个大小为15K字节的文件A。操作系统和文件系统会把文件路径转换为具体的LBA地址,可能最终转换为读取硬盘上从B扇区开始的4个扇区(按照每个扇区4KB计算)。然而,前面我们说了,CPU并不能直接访问硬盘,因此需要先把这四个扇区的数据,传输到内存中。存放这四个扇区数据的内存,就是Buffer。忽略CPU内部的Cache机制,CPU现在可以对这一段内存以字节为单位进行操作,在所有操作完成后,Buffer所占用的内存会被回收。
写入则是相反,应用程序需要先在内存中准备好这四个扇区的数据,然后硬盘控制器会把这些数据原样写入到硬盘对应的扇区上。同样的,写入完成后Buffer所占用的内存也会被回收。
除了用于临时存放IO设备上的数据,Buffer通常还有其它几种用途:
1,把多次小量数据传输合并为更少次数的批量数据传输, 减少传输过程本身的额外开销;
2,为两个不能直接交换数据的传输进程的提供临时中介存储;
3,确保组成单次传输规定的最小单位
4,对大块数据进行组装或者分解
如果这个应用需要频繁读取文件A,每次都从硬盘读取显然会很慢。如果第一次读取完成后,不直接清空Buffer所占用的内存,而是把这段内存保留下来或者先复制到其它内存地址,以后对这个文件的读取就可以直接从内存访问,无需再次从硬盘读取,应用程序的性能就会快很多。这才是Cache,严谨点来说,这是Read Cache,所以台湾把Cache翻译为“快取”,更多的是指Read Cache。但是,并不是所有从硬盘上读取到Buffer的数据都会被Cache的,例如复制一个包含多个数GB的视频文件的文件夹,通常只有这个文件夹的数据会被Cache,而每个具体的视频文件的数据都不会进入Cache。
有Read Cache自然也有Write Cache。还是这个占用四个扇区的文件,假如应用程序需要先更改第一个扇区的内容并写入硬盘,过一段时间再更改第三个扇区的内容并写入硬盘。这样需要对硬盘进行两次写入。但如果第一次应用要求写入的时候,操作系统只是把这个文件的数据写入到内存中并返回写入完成的响应,但数据并没有真正写入硬盘。等收到后续写入请求的时候才真正写入硬盘,则只需要进行一次写入。通过这样的方式,根据实际情况可能实现:
1,应用程序无需等待真正的写入完成即可继续后续操作,提高应用程序性能;
2,减少写入次数;
3,把多个小数据量的写入合并成一个大数据量的写入;
4,把多个随机写入转换为持续写入。
这几种情况中的一种或者多种,从而提高IO性能。但是,对于首次写入来说,这个性能是必然更低的——假设直接写入需要0.02秒,因为要等待后续的写入请求,可能从发起首次写入请求,到数据真正写入硬盘用了0.5秒。这就是国内把Cache翻译为“缓存”的原因——暂缓存储。所以其实“缓存”和“快取”都只是表达了一半的意思,不存在说“快取”比“缓存”翻译的更好——虽然大部分时候Read Cache比Write Cache更常见。
需要另外提一下的是,Write Cache同时也是Buffer的一种形式,在数据写入到硬盘前,是不能被回收的。
最后,Read Cache和Write Cache并不是严格分离的。很多时候Write Cache同时也可以作为Read Cache使用,但在分布式系统中,则需要考虑Cache一致性的问题。