本文主要是总结Linux内存的PageCache 原理,并观察ORACLE、MySQL、PG数据库的内存使用特点。文中Linux系统是CentOS 7.9,内核版本3.10.0-1160 。服务器内存大小512GB。
有关Linux内存的源码解读和原理总结的书非常多且厚。这里只是就实际工作中的疑点尝试进行分析总结。所以不会那么系统和完备。说内存之前其实先从磁盘说起。以前机械盘读取速度实在太慢,现在的SSD虽然快很多了,能到几十万的 IOPS,但是相比内存的读写速度那还是慢多了。部署在磁盘上的文件系统为提升自己的读写性能,降低磁盘I/O的慢速影响,文件系统对读写的页支持缓存。这个缓存是从内存里借来的,体现为Page
Cache。(这里我不关注Page
Cache跟Buffer
Cache的历史)。而数据库部署在文件系统上的I/O有的会用文件系统这个缓存特性,有的又不用这个特性。加上成熟的数据库还会有自己的缓存,这使得内存成为影响数据库性能结果的重要因素之一(其他还有磁盘)。而我一直好奇的就是想知道Page
Cache里具体缓存哪些内容。Cache 大小查看
通常查看 PageCache的大小方法是 free 命令中的 Cache字段。这个字段实际是Buffer
cache和Page cache的值统计在一起,这里有个历史原因。不过由于buffer cache大小一般很小(1个G不到),所以这里不关心buffer
cache的数据。Free 命令的帮助文档对 Cache 的解释细节过于简单,说是从 /proc/meminfo 取的。这里我们换另外一个命令atop的 cache 字段查看Page
Cache 大小。atop 命令的帮助文档里对Cache的解释是包含resident shared memory 在内的用作文件系统缓存的内存。而 atop的 shmem 字段则显示了包括tmpfs在内的共享内存大小。所以初步看来,Cache包含文件的缓存和共享内存。
查看文件缓存的工具
pcstat 命令
网上查看文件缓存的工具有pcstat。项目地址:https://github.com/tobert/pcstat 。比如说用 pcstat 可以查看常见的lib文件的缓存情况。Cached 字段表示缓存的页数量(页大小是4KB),Percent是每个文件内容在缓存中的比例。这些Lib文件很多,但每个都不是很大,所以总的大小也不过几个GB,也不是我关注的重点。我们换个别的文件例子。果然,数据文件和Redo日志文件都在Page Cache里缓存着。这个有点坑,不过不是本文重点。上面是已知文件路径去查文件的缓存情况。我想直接查某个进程所有文件缓存情况,或者直接查看Page
Cache里所有缓存。这时候又有一个命令hcache。hcache 命令
hcache 开源项目地址:https://github.com/silenceshell/hcache下面查看ORACLE的读写用的进程,看看这些进程打开的文件的缓存情况。这个工具想法是好的,但是经常报错(/proc/进程id/下文件读取报错)。最重要的是它找出来的文件都是一些 Lib 文件,看不到数据库文件。查看了源码发现,它是读取进程的虚拟内存中的映射文件,然后将文件传递给pcstat去显示。有关进程虚拟内存和内存映射后面再介绍,显然这个命令还不是我想要的。根据 pcstat的项目说明,它是参考项目linux-ftools写的。我们再看看这个工具。linux-ftools 工具
·linux-fincore : 用于查看文件的缓存情况,可以设置过滤条件。·linux-fadvise :用于干预一个文件的缓存策略。这个跟系统调用 fadvise函数类似。有关干预策略后面再单独说。·linux-fallocate:用于快速扩大文件的逻辑空间。这个方式产生的文件里面主要是空洞,压缩比非常高。这个以后单独写文章再介绍。实际使用后,感觉linux-fincore参数比较实用。不过遗憾的是它不能直接查看Page
Cache 里的缓存的内容,或者根据进程ID查看。这两个需求里前面那个实现有点困难,但后面这个还是比较容易的。根据进程ID可以查看进程打开的文件列表,将列表传递给 linux-fincore就可以查看。前面那个需求后来有网友写脚本变相实现了。查看所有进程,根据进程实际内存(RSS)大小排序,取前N个进程,查看其打开的文件缓存,基本上能确定Page Cache中占比比较大的文件信息了。脚本我做了小的修改(去重)。[root@sfx110008 ~]#!/bin/bash
if [ ! -f /usr/local/bin/linux-fincore ]then echo "You haven't installed linux-fincore yet" exitfi
ps -e -o pid,rss|sort -nk2 -r|head -50 |awk '{print $1}'>/tmp/cache.pids
if [ -f /tmp/cache.files ]then echo "the cache.files is exist, removing now " rm -f /tmp/cache.filesfi
while read linedo lsof -p $line 2>/dev/null|awk '{print $9}' >>/tmp/cache.filesdone </tmp/cache.pids
if [ -f /tmp/cache.fincore ]then echo "the cache.fincore is exist, removing now"
rm -f /tmp/cache.fincorefi
for i in `cat /tmp/cache.files | sort |uniq `do
if [ -f $i ] then echo $i >>/tmp/cache.fincore fidone
linux-fincore --only-cached -C 1048576 -s `cat /tmp/cache.fincore`
rm -f /tmp/cache.{pids,files,fincore}
使用这个脚本我们再查看一次当前系统中文件缓存状况。看上面汇总的Page
Cache 大小是207,596,109,824(约194GB),即使我查看全部的进程汇总得到的值也差不多。跟free 和 atop看到总的cache大小(347G)相差还是很大。大概相差了 150GB 左右。这个差异数据就是共享内存部分,正是free的shared字段,也是atop的shmem字段。还是ORACLE 的 SGA 内存大小。查看ORACLE的参数、共享内存段大小,数据吻合。然而,如果你看你的ORACLE环境,看到的现象可能跟我这里还有点区别。一方面当进程关闭了文件后,这个脚本就看不到这个文件的缓存情况,而文件还在Page
Cache里缓存。另外一方面这还跟ORACLE的共享内存实现机制有关系,这个下篇文章再介绍。我们先继续就查看Page Cache中文件继续展开。ORACLE的表全部在表空间里,所以看到表空间的缓存情况就够了。当然实际上文件系统上的ORACLE都会改为Direct I/O,表空间的文件也就不会缓存了。但是PostgreSQL 数据库至今还不支持Direct I/O。加上PG里的表是单独的文件,并且大表还会按大小切割为很多小文件,文件的缓存状况对数据库的性能还会有影响。这个工具脚本作用就大了。PostgreSQL数据库表文件缓存状况
对一个正在做读写测试的PostgreSQL实例使用上面脚本查看缓存的文件信息,内容非常多,取头部和尾部信息看看。不过这里不容易看出是什么表,只能看到数字。当然根据数字到PG元数据库里关联查询也能确定是哪个库哪个表。不过PG也提供了一个数据库扩展,可以查看一个表的缓存情况。看名字实现原理跟 linux-fincore类似。pgfincore 扩展
pgfincore 开源项目地址: https://github.com/klando/pgfincore 。编译安装后,在每个db上创建扩展。然后就可以查询了。pgfincore 扩展还提供了多个函数实现类似 linux_fadvise 功能,可以对一个表在Page
Cache的缓存策略进行干预。这个可以用于数据库重启后的重要大表的快速预热,或者将某个大表从缓存中踢出。在OS里用 linux_fincore命令可以核实一下这个结果。PG数据库主机的Cache大小也远远大于上面脚本统计的大小,其原因也类似,跟共享内存有关。这个放到下篇介绍。
原文作者:数据库技术闲谈;如有侵权,请联系管理员删除。