Linux内核技术4 Page Cache - Page Cache容易回收导致的业务性能问题

555 阅读4分钟

Page Cache是Linux中很重要的一个部分,因此它的难以回收或者容易回收都可能会导致性能问题。

两种误删PC的场景 人为与OS
1.误操作而导致 Page Cache 被回收掉,进而导致业务性能下降明显;

inode
inode是内存中对磁盘文件的索引,进程在查找或者读取文件时就是通过 inode 来进行操作的。

进程查找文件过程
1.进程会通过 inode 来找到文件的地址空间(address_space) 2.结合文件偏移(会转换成 page index)来找具体的 Page。 3.如果该 Page 存在,那就说明文件内容已经被读取到了内存;如果该 Page 不存在那就说明不在内存中,需要到磁盘中去读取。

image.png

drop_cache
drop_cache命令可以用于释放inode, 并且我们可以通过写入不同的数值来释放不同类型的 cache(用户数据 Page Cache,内核数据 Slab,或者二者都释放) image.png

谨慎使用drop_cache
在系统内存紧张的时候,运维人员或者开发人员会想要通过 drop_caches 的方式来释放一 些内存,但是由于他们清楚 Page Cache 被释放掉会影响业务性能,所以就期望只去 drop slab 而不去 drop pagecache。于是很多人这个时候就运行 echo 2 > /proc/sys/vm/drop_caches>

但实际上,当我们执行 echo 2 来 drop slab 的时候,它也会把 Page Cache 给 drop掉,从而导致性能下降。

如何观察是否有人或者有程序执行过 drop_caches

[root@yz117 ~]# grep drop /proc/vmstat 
drop_pagecache 0 
drop_slab 0

2.内核的一些机制导致业务 Page Cache 被回收,从而引起性能下降。
在内存紧张的时候会触发内存回收,内存回收会尝试去回收 reclaimable(可以被回收的)内存,这部分内存既包含 Page Cache 又包含 reclaimable kernel memory(比如 slab)

内存回收过程
1.Reclaimer 是指回收者,它可以是内核线程(包括 kswapd)也可以是用户线程。 2.回收的时候,它会依次来扫描 pagecache page 和 slab page 中有哪些可以被回收的. 3.如果有的话就会尝试去回收,如果没有的话就跳过。 4.在扫描可回收 page 的过程中回收者一开始扫描的较少,然后逐渐增加扫描比例直至全部都被扫描完。

relcaim slab过程带来的性能问题 如果 inode 被回收的话,那么它对应的 Page Cache 也都会被回收掉,所以如果业务进程读取 的文件对应的 inode 被回收了,那么该文件所有的 Page Cache 都会被释放掉,这也是容 易引起性能问题的地方。

image.png

如何观察

$ grep inodesteal /proc/vmstat 
 pginodesteal 114341
 kswapd_inodesteal 1291853
 
kswapd_inodesteal 是指在 kswapd 回收的过程中,因为回收 inode 而释放的 pagecache page 个数;
pginodesteal 是指 kswapd 之外其他线程在回收过程中,因为回收 inode 而释放的pagecache page 个数。

如何避免 Page Cache 被回收而引起的性能问题?

1.从应用代码层面来优化;
思路是从应用代码层面对PC进行区别对待,重要的PG保护起来,不重要的则随时可以被回收。

比如对于重要的数据,可以通过 mlock(2) 来保护它,防止被回收以及被 drop;对于不重要的数据(比如日志),那可以通过 madvise(2) 告诉内核来立即释放这些 Page Cache。

/* Lock these memory to prevent from being reclaimed */
 mlock(p, SIZE);

2.从系统层面来调整。
memory cgroup protection
实际上linux提供了不用修改源码就能实现PC保护的方式,也就是划分水位。

memory.max
这是指 memory cgroup 内的进程最多能够分配的内存,如果不设置的话,就默认不做 内存大小的限制。

memory.high
如果设置了这一项,当 memory cgroup 内进程的内存使用量超过了该值后就会立即被 回收掉,所以这一项的目的是为了尽快的回收掉不活跃的 Page Cache。

memory.low
这一项是用来保护重要数据的,当 memory cgroup 内进程的内存使用量低于了该值 后,在内存紧张触发回收后就会先去回收不属于该 memory cgroup 的 Page Cache, 等到其他的 Page Cache 都被回收掉后再来回收这些 Page Cache。

memory.min
这一项同样是用来保护重要数据的,只不过与 memoy.low 有所不同的是,当 memory cgroup 内进程的内存使用量低于该值后,即使其他不在该 memory cgroup 内的 Page Cache 都被回收完了也不会去回收这些 Page Cache,可以理解为这是用来保护 最高优先级的数据的。

image.png

总结
如果你想要保护你的 Page Cache 不被回收,你就可以考虑将你的业务进程放在一 个 memory cgroup 中,然后设置 memory.{min,low} 来进行保护;与之相反,如果你 想要尽快释放你的 Page Cache,那你可以考虑设置 memory.high 来及时的释放掉不活 跃的 Page Cache。