Linux PageCache详解与性能调优

562人浏览   2024-05-04 19:09:14

前言

Linux系统经常出现空闲内存少,如同内存不够用一样。其实这是Linux系统内存管理的一个优秀特性:Linux尽量充分利用系统内存。比如:将被访问过的硬盘数据加载到内存(buffer/Cache),利用内存高速读写的特性来提供Linux系统高效率的数据访问能力。


Page Cache概述

应用程序读取Linux文件的两种方式:read、mmap。无论哪种,Linux内核都会相应申请一块Page cache用来缓存文件内容。后续再次读取就直接从Page cache获取,不用访问磁盘文件,以此来提升Linux系统性能。

Linux系统的PageCache通过free命令查看,如图:

cache :表示已读取的数据(快取)
buffer:表示要写入的数据 (缓存)

概念上的差别:cache是针对文件的,buffer是针对磁盘块数据的。


Page Cache的意义

实际案例测试:

首先,提前生成一个大小为4G的文件;测试文件的读取耗时;然后清空Page cache,再次测试文件的读取耗时,两者耗时对比。

1. 生成一个大小为4G的文件

[root@felixzh home]# dd if=/dev/zero of=/home/felixzh_dd.out bs=4096 count=1048576 status=progress

2. 测试文件的读取耗时:

[root@felixzh home]# time cat /home/felixzh_dd.out &> /dev/null

3. 清空Page Cache

[root@felixzh home]# sync && echo 3 > /proc/sys/vm/drop_caches

4. 再次测试文件读取文件的耗时:

测试结果表明:第一次耗时远小于第二次;原因是生成文件时,已经缓存在cache。第一次读取的是内存数据。清空cache后,再次读取则是从磁盘上读取。所以Page Cache存在的意义:减小磁盘I/O,提升应用I/O速度。

Page Cache的产生

两种方式:

Buffered I/O(标准I/O)
Memory-Mapped I/O(储存映射IO)

  1. 存储映射I/O直接将Page映射到用户空间,用户直接读写PageCache。
  2. 标准I/O首先写用户缓存区(User page),然后再将用户缓存区里的数据拷贝到内核缓存区(Pagecahe Page)。类似的,读操作先将内核缓存区拷贝到用户缓存区,然后从用户缓存区读取数据。

所以说,存储映射I/O要比标准的I/O效率高一些,毕竟少了“用户空间到内核空间互相拷贝”的过程。

下图简单描述标准I/O写操作流程:

Linux系统提供了脏页同步到磁盘的相关参数:

[root@felixzh home]#  cat /proc/vmstat | egrep "dirty|writeback"

其中:

nr_dirty     #表示系统中脏页数量(单位为Page 4KB)
nr_writeback #表示正在回写到磁盘的脏页数量(单位为Page 4KB)

Page Cache的死亡

上面提到的使用free命令,查看到的buffer/cache,表示“活着”的Page Cache。什么时候回收呢?两种触发方式:后台回收、直接回收。

借助sar工具可以很直观地观察Page cache的回收行为:

pgscank/skswapd(后台回收线程)每秒扫面的Page个数
pgscand/sApplication在内存申请过程中每秒直接扫描的Page个数
pgsteal/s:扫面的page中每秒被回收的个数
%vmeffpgsteal/(pgscank+pgscand),回收效率,越接近100说明系统越安全,越接近0,说明系统内存压力越大
pgpgin/s 表示每秒从磁盘或SWAP置换到内存的字节数(KB)
pgpgout/s: 表示每秒从内存置换到磁盘或SWAP的字节数(KB)
fault/s: 每秒钟系统产生的缺页数,即主缺页与次缺页之和(major + minor)
majflt/s: 每秒钟产生的主缺页数.
pgfree/s: 每秒被放入空闲队列中的页个数

性能调优

Linux提供影响脏页数据回写的相关参数:

[root@felixzh home]# sysctl -a | grep dirty

vm.dirty_background_ratio    触发回刷的脏数据占用内存的百分比         
vm.dirty_background_bytes    触发回刷的脏数据量                     
vm.dirty_bytes               触发同步写的脏数据量                    
vm.dirty_ratio               触发同步写的脏数据占可用内存的百分比      
vm.dirty_expire_centisecs    脏数据超时回刷时间(单位:1/100S)       
vm.dirty_writeback_centisecs 回刷进程定时唤醒时间

如果dirty_ratio大于dirty_background_ratio,是不是就不会达到dirty_ratio呢?首先达到dirty_background_ratio条件,会触发flush进程进行异步的回写操作,此时应用进程仍然可以进行写操作,如果应用进程写入量大于flush进程刷出量。那自然就会达到vm.dirty_ratio这个参数所设定的阙值,此时操作系统会转入同步地进行脏页的过程,阻塞应用进程。


配置实例

场景1:尽可能不丢数据

dirty_background_ratio = 5
dirty_ratio = 10
dirty_writeback_centisecs = 50
dirty_expire_centisecs = 100

此配置通过减少Cache,更加频繁唤醒回刷进程的方式,尽可能让数据回刷。

场景2:追求更高性能

dirty_background_ratio = 50
dirty_ratio = 80
dirty_writeback_centisecs = 2000
dirty_expire_centisecs = 12000

此配置通过增大Cache,延迟回刷唤醒时间来尽可能缓存更多数据,进而实现提高性能。

场景3:突然的IO峰值拖慢整体性能

所谓IO峰值:突发的大量数据写入,导致瞬间IO压力飙升,导致瞬间IO性能狂跌。

dirty_background_ratio = 5
dirty_ratio = 80
dirty_writeback_centisecs = 500
dirty_expire_centisecs = 3000

此配置通过增大Cache和更加频繁唤醒回刷的方式,解决IO峰值的问题。通过保证脏数据比例保持在一个比较低的水平,当突然出现峰值,也有足够的Cache来缓存数据。


Linux内核为每一个块设备分配一个内核线程(flusher threads),线程名为“Writeback",执行体为"wb_workfn",通过workqueue机制实现调度。


总结

Page Cache是一项重要的性能改进:读缓存在绝大多数情况下是有益无害的;写缓存,内核过段时间再异步刷新到磁盘,对加速磁盘I/O有很好的效果,但是当数据未写入磁盘发生宕机时,可能造成数据丢失。

相关推荐