第三章主题:操作系统缓存(Linux背景下)
操作系统的缓存机制
Linux 上有页面缓存(page cache) 、文件缓存(file cache) 、缓冲区缓存(buffer cache) 这些机制。
本章讲述页面缓存。
由于操作系统将物理硬件抽象化,因此才产生了虚拟内存。
(交换文件是运用了虚拟内存的一个功能,当物理内存不足时,将次级存储( 主要是磁盘)看作内存,以从表面上解决内存不足。)
操作系统并不直接把内存传给进程,而是先在内核中将内存抽象化。这就是虚拟内存的机制。这样可以使进程无需考虑自己使用的内存位于什么位置,可以认为必然从0x000地址开始,这样处理就能更方便。
所以下图中1和2的地址并不相同、
操作系统会一次性读出磁盘上的内容,类似的虚拟内存机制给进程分配内存的时候也不是一个字节一个字节地访问,而是分配好适当的大小(4kb左右)并传递给进程。这样的一个内存块称为“页面”。进程要求分配内存时,操作系统就分配足够的页面(至少一个)并将其传递给进程。进程是无法直接访问磁盘的,只能访问(虚拟)内存。
虚拟内存:
*具有让进程更容易地访问内存等好处
*操作系统在内核中将内存抽象化
*操作系统以页面为单位分配物理内存并管理
=====
进程从磁盘中读取数据的过程:
操作系统首先从磁盘中读取4kb块。读出的内容必须像图3.3 的②那样先放在内存上,这是因为进程是无法直接访问磁盘的。毕竟,进程能访问到的只有(虚拟)内存。因此,操作系统如图3.3 的②那样将读出的块写入内存。然后,操作系统将该内存的地址(如图3.3 的③所示) (变换成虚拟地址后)告诉进程〈1〉。然后进程再访问内存③, (如图3.3 的④所示)。
进程读完数据后,虽然读出的数据已经全部处理完,而不再需要这块内存,但并不会马上释放掉,而是保留下来。这样,下次其他进程想访问同一块磁盘的该内存时,就可以直接使用留下来的页面,而无需再次访问磁盘。这就是页面缓存。也就是说,内核分配过的内存不会释放,而是一直保留下来,这就是页面缓存的基本原理。
除了某些特殊情况(例外情况就是负责磁盘缓存的部分,VFS),页面缓存都会透明的应用在所有的I/O上。因此一直运行的操作系统更快。
====
VFS
VFS(virtual file system),虚拟文件系统,不论使用什么文件系统,从哪个磁盘读出,必定会通过同样的机制进行缓存。
VFS负责将不同实现方式的文件系统抽象化,并实现了页面缓存以提高性能。
Linux使用inode编号和内容在文件中位置的偏移量两个值作为键进行缓存。操作系统内部使用了Radix Tree的数据结构来进行对缓存。
LRU
Least Recently Used,放弃最老的内容。
====
Linux会把全部空闲内存用于缓存,当进程请求分配内存时,如果缓存导致内存没有空闲,就先删除旧的缓存,再为进程分配内存。
以缓存为前提降低I/O负载的策略
由之前的分析可知,缓存策略可减少磁盘I/O的次数,既减轻了负载压力,也加快了运行速度。以缓存为前提降低I/O负载有两个要点:
1.如果物理内存比数据规模还大,则考虑全部缓存(还可以使用压缩算法)
2.与经济成本的平衡性
如果缓存不过来,可以扩展到多台服务器:
CPU负载分散只需要简单地增加服务器即可
I/O分散要考虑局部性
====
如果无法增加内存,可以考虑将数据分割,分别放在不同的服务器上。如果能完美地分割数据的话,磁盘I/O次数不仅会与增加的服务器数量成比例下降相当的次数,而且缓存中保存的数据比例还会增加,能够极大地提高吞吐量。
在MySQL 等数据库中处理大规模数据时要注意这一点。例如,系统维护等重启了服务器,那么之前内存中缓存的页面就全部被清空了。
所以有时服务器重启后,会先让数据库将必要的数据读取一次之后再投入生产环境。
利用局部性的分布式
1.以表为单位进行分割,放入不同的数据库服务器。相应地,修改应用程序,使得对应的请求发送到对应的数据库服务器中。
2.“从数据的中间开始分割”,如按照首字母,不同范围的数据放入不同的数据库服务器。
根据访问模式实施分布式
3.根据用途将系统分成“岛”。根据HTTP请求的User-agent和URL进行分割。
搜索引擎的爬虫的特点是,它会访问很久很久以前的网页。人类用户基本不会去访问那些页面,但爬虫仍会访问,而且访问范围也十分宽广。这种情况下缓存很难奏效。只有在大量请求同一页面时,才容易通过缓存提高性能,对于这种大范围的访问,缓存也无能为力。但是,爬虫对应答速度的要求也不高,所以将其分到单独的岛上。
相反,爬虫之外的访问,即一般用户的访问通常都集中在首页或热门链接页面等最新或最受欢迎的页面上,访问十分频繁,很容易缓存。
这样,将容易缓存的请求和不容易缓存的请求放在不同的岛上处理,前者的局部性使得缓存内容更稳定,能达到较高的缓存率。而后者的请求会扰乱前者的缓存,所以如果不分在单独的岛上,而是放在一起的话,整体的缓存效率就会降低。