free命令

free命令用于查看系统内存信息

常用选项:

-m:以MB为单位显示

-g:以GB为单位显示

-h:以人类易读的方式显示

运行示例:

Linux读缓存/数据回写机制_数据回写

运行结果的前面3 个字段很容易理解,但是used + free 的值显然不等于total,这就要看后面两个字段了,shared 为共享内存,进程间通讯使用,buff/cache 叫缓存,从红帽7 开始,buff 和 cache 这两个值合并为一个字段显示,以前是分开的,区别在于buff 是inode 索引的缓存,cache 是data 数据的缓存,buff/cache 是读缓存,我们经常做的一件事情是提升内存的读性能通过读缓存来实现,读cache 提升读性能,写cache 提升写性能,看起来是一句废话,那它们是如何实现的呢?

读cache 提升读性能

当从硬盘中读取数据时,会先将数据读到内存中,当数据处理完成后,依然将数据保留在内存中,下次需要调用这些数据时,就直接从内存中读取,而不需要从硬盘中读取,提升读性能。

我们可以做一个这样的实验:

传递这个内核参数,清理缓存区的数据:

sysctl -w vm.drop_caches=3
  • 1.

Linux读缓存/数据回写机制_缓存_02

在任何命令前,加一个time,这样可以计算执行后面的命令,需要多长时间。

# time cp -r /usr/ /tmp/
  • 1.

Linux读缓存/数据回写机制_内存_03

Linux读缓存/数据回写机制_数据回写_04

这个时候,/usr 目录4.6G 的数据,几乎全部写进了buff/cache缓存区,内存资源被消耗殆尽,这个数据即便已经写完了,但会一直保留在缓存中。

再建一个文件夹,我们再拷贝一次:

# mkdir /redhat
# time cp -r /usr/ /redhat/
  • 1.
  • 2.

Linux读缓存/数据回写机制_数据回写_05

这个时候,往/redhat 目录拷贝/usr 的数据,系统会直接从缓存中读取,所以运行速度相比第一次更快了一点,看起来提升并不是很明显,但是如果服务器上有一个数据会被访问一千次呢?那么这个读缓存就非常有意义了!

那么问题来了,这个数据会在内存中缓存多久呢?这个问题非常好!

只要内存不紧张,它会一直保留着,当内存紧张的时候,自动释放。这样可以提升读性能。

通常有两种情况,我们需要释放缓存:

1、基线测试(baseline test)

什么叫基线测试,比如说我想测一下磁盘的性能,这个时候如果是因为缓存没有释放掉,测试的读取速度是读缓存区的数据会导致测试结果不准确。这个时候可以手动清理缓存,执行sysctl -w vm.drop_caches=3,这个内核参数看起来很奇怪,为什么值等于3,而不是1或2呢?这是历史遗留问题,1是清空buff,2是清空cache,3是把这两个都清空,但是因为现在buff/cache合并为一个字段了,所以也是3。

Linux读缓存/数据回写机制_数据回写_06

再来看,缓存区已经清理完成了,但为什么没有被清成0 呢?因为有些缓存数据可能仍然正在被使用。

有些系统执行这条手动清理的命令可能不会生效,这个时候应该检查下面这个内核参数值:

Linux读缓存/数据回写机制_内存_07

特别是在安装了容器的机器上,有些教程可能会让我们把这个内核参数设置成0,这个取值范围是0-100,如果设置成0,那么即使是内存紧张的时候,缓存区的数据也不会被释放。所以这个内核参数调整的是:当系统内存资源紧张的时候,是更倾向于把它释放掉,还是倾向于保留。

2、机器休眠

机器在做休眠的时候,系统会把缓存区的数据写回到硬盘,这个过程影响休眠的速度,因此可以提前把数据清理掉。

写cache 提升写性能

当写入数据的时候,先将数据写入到内存中,在内存中将不连续的IO 整合成连续的IO,再一次地写入硬盘。这种数据写入方式类似于电梯调度算法,举例来说:

第一个IO请求:往磁盘第50扇区写入数据

第二个IO请求:写第10000个扇区

第三个IO请求:写第600个扇区

第四个IO请求:写11000个扇区

这个时候,如果没有写缓存,按照顺序写入的方法,磁盘的磁头移动到第50个扇区,然后移动到第10000个扇区,再移回第600个扇区... 磁头来回移动,混乱无序,这样来写入数据效率是非常低效的。这里又涉及到两个概念了,回写与透写:

回写:数据先写缓存,经过整合以后再写硬盘;

透写:数据不写缓存,直接写硬盘。

如果是用回写的方式,数据写缓存经过整合以后,写入到磁盘的时候先处理第一个IO请求,然后处理第三个、第二个、第四个。所以从硬件的角度来看:不是写缓存的方式提升了写性能,而是数据整合。

但是回写有个前提条件:缓存(内存)的写入速度要比主存(硬盘)快,这样才有意义。

来做一个小实验:

往/tmp 目录写入一个文件:

# time dd if=/dev/zero of=/tmp/test1 bs=4k count=102400
  • 1.

Linux读缓存/数据回写机制_dirty data_08

写入时间只花了不到一秒钟,非常快...

但如果,给dd 命令加一个参数:oflag=direct,这个作用是直接写数据到硬盘,不写缓存。

# time dd if=/dev/zero of=/tmp/test1 bs=4k count=102400 oflag=direct
  • 1.

Linux读缓存/数据回写机制_缓存_09

这次花了20多秒,相比之下慢了很多。

那么,同样的问题再次出现:写入的数据会在缓存中保留多久呢?

写缓存也可以和读缓存一样,只要内存不紧张,一直保留在里面吗?

显然是不可以,因为这些数据都属于脏数据(在内存中已经发生变化,还未写入硬盘的数据),一旦出现异常(掉电、死机等情况),将导致数据丢失。

那多久写入硬盘呢?实时吗?肯定不是,因为这样就无法进行整合了。那保留10分钟?这样风险太大。Linux系统中,这个值默认是30秒。

Linux读缓存/数据回写机制_脏数据_10

这个值的单位很奇怪,不是毫秒,也不是秒,而是百分之一秒,3000即是30秒。

这个值可以更改,通过写入到/etc/sysctl.conf 文件的方式:

# echo 'vm.dirty_expire_centisecs = 2000' >> /etc/sysctl.conf
  • 1.

Linux读缓存/数据回写机制_脏数据_11

那么如何测试这个值呢?在系统的/proc/meminfo 文件中,有一行数据:

Linux读缓存/数据回写机制_脏数据_12

通过监控这个值,就可以验证回写等待时间了。

如果你想实时查看这个值,可以这样写:

# watch -n 1 -x grep Dirty /proc/meminfo
  • 1.

-n 指定间隔时间,单位是秒,后面的数值可以是小数,写1表示每一秒钟刷新一次后面的命令执行的结果。

另外:还有一个内核参数需要关注,vm.dirty_writeback_centisecs 表示检测的间隔时间,默认是500,单位也是百分之一秒,就是每隔5 秒检测一次。

如果是随机小IO 场景,应该是将回写保留时间更长一点?还是更短一点?

建议更长一点,因为这样IO 会整合得更充分,如果是大IO,建议缩短。

这就是内存的脏页回收机制,提升了写性能。

但是这样存在一个问题:万一真的停电了,数据不就丢失了吗?是的,如果发生断电,就真的完蛋了...

机房的服务器,通常都有后备电池,可以让你来得及正常下电关机。

那万一存储停电了呢

存储设备也有缓存吗?有的,而且存储设备的缓存就更大了,可能很多设备都在往里面写入数据,几十GB 的数据都在里面。一般存储设备里面都有一个硬件叫BBU(后备电池单元),平时处于充电状态,一旦设备断电了,BBU可以给缓存供电,但BBU很小,它没办法给所有的硬盘供电,只能给缓存供电,可是如果BBU的电耗尽了,电力还没有恢复怎么办呢?存储设备还有保险箱盘,BBU可以给缓存和保险箱盘供电,保险箱盘就是存储里面装操作系统用的那块盘,当存储设备断电时,BBU会给缓存和保险箱盘供电,先把缓存区的数据有序地写入到保险箱盘暂停,即便BBU的电耗尽了,缓存区的数据也已经落盘到保险箱盘了,电力恢复以后,从保险箱盘的一个独立的区域里面把缓存的数据重新读到缓存区,再写回到应该落盘的位置。

所以如果某一天,你发现公司的存储设备怎么性能突然变得这么差,排查半天发现BBU故障了, BBU故障怎么会导致性能下降呢?还真会!

存储设备的LUN 写策略有:

  1. 回写(默认)
  2. 透写
  3. 强制回写

当BBU 故障时,为了保证数据的安全,牺牲性能,所以写策略自动变成透写。

如果BBU一时修不好,你又不怕死,可以人工调整为强制回写,保证性能。

如果是分布式存储,如何实现缓存?是放在内存中吗?不会的。为什么?因为数据量太大了,写内存会让系统崩溃。在分布式存储中,有专用的缓存盘,一般缓存盘通常采用NVME。

下图是华为的Fusion Storage分布式存储解决方案,了解一下即可:

Linux读缓存/数据回写机制_内存_13

Linux读缓存/数据回写机制_dirty data_14

拓展

1、如果,内存中有脏数据还未落盘我又不想等待自动回写,可以执行sync命令手动回写脏数据。

2、下面这个内核参数,值为30,这是一个百分比,即30%,表示:如果脏数据占用缓存区30%的空间,就开始回写数据,即使没有达到超时时间。

Linux读缓存/数据回写机制_dirty data_15

3、shutdown、reboot、init 0、poweroff 等等关机/重启命令默认会包含有sync 这个动作,对于一台高速运行、处理高并发业务的主机,如数据库,千万不要执行强制关机,否则,大量的脏数据未落盘,这些数据都会丢失,造成不可估量的损失!!!