04 脏数据对容器读写性能的影响

一 引入问题

1、思考一个问题

有一个容器,里面运行的应用需要经常进行数据的读写,每次读写数据的量不大,但是数据累积的总量会比较大,比如每次读写不超过1G,但是数据写入磁盘的总量累计可能超过10G

2、问:你如果为该容器分配资源?

很多人都会觉得,只需要保证磁盘空间够用就可以了,不需要考虑内存,这其实是有问题的,此时如果内存在1G左右,你会发现容器的整体读写效率都很低

3、为何会出现这种情况呢?

二 储备知识

2.1 Bufferd IO与Direct IO

linux io的两种类型:bufferd I/O(先写buffer后由内核异步把脏数据刷入磁盘) 与 direct I/O(直接写入)

什么是脏数据?

针对bufferd I/O,写数据是先写入了page cache,还没来得及写入磁盘,这部分数据称之为脏数据,即dirty pages,

脏数据脏数据何时会落盘???

在linux内核中会有专门的内核线程(每个磁盘设备对应的kworker/flush线程)会定期负责把脏数据落到磁盘中。

因为bufferd IO的读写效率更高,所以大多数场景使用的都是该模式。

但该模式下,关于读和写需要考虑的内容还是有区别的

  • 1、读缓存在绝大多数情况下是有益无害的(程序可以直接从RAM中读取数据)。

  • 2、写缓存比较复杂

    • (1)Linux内核将磁盘写入缓存,过段时间再异步将它们刷新到磁盘。这对加速磁盘I/O有很好的效果,但是当数据未写入磁盘时,丢失数据的可能性会增加。

    • (2)也存在缓存被写爆的情况。

    • (3)还可能出现一次性往磁盘写入过多数据,以致使系统卡顿。这些卡顿是因为系统认为,缓存太大用异步的方式来不及把它们都写进磁盘,于是切换到同步的方式写入。

针对写缓存的这些诸多问题,如何解决呢?我们来看几个重要的内存参数

2.2 几个重要的内核参数

$ sysctl -a | grep dirty
vm.dirty_background_bytes = 0  # 配置路径:/proc/sys/vm/dirty_background_bytes
vm.dirty_background_ratio = 10 # 配置路径:/proc/sys/vm/dirty_background_ratio

vm.dirty_bytes = 0             # 配置路径:/proc/sys/vm/dirty_bytes
vm.dirty_ratio = 20            # 配置路径:/proc/sys/vm/dirty_ratio

vm.dirty_writeback_centisecs = 500 # 配置路径: /proc/sys/vm/dirty_writeback_centisecs
vm.dirty_expire_centisecs = 3000   # 配置路径: /proc/sys/vm/dirty_expire_centisecs 

vm.dirtytime_expire_seconds = 43200 # 配置路径: /proc/sys/vm/dirtytime_expire_seconds

补充:
/proc/sys/vm/dirty_*, 在容器和宿主机上是一样的,这些值没有namespace.

下面几个内核参数,原理大致相同,都是控制什么时候就刷一波dirty page,防止它一直增长把available内存吃满

  • vm.dirty_background_ratio 是内存可以填充脏数据的百分比。这些脏数据稍后会写入磁盘,pdflush/flush/kdmflush这些后台进程会稍后清理脏数据。比如,该值设为10,那么我有32G内存,则允许最大有3.2G的脏数据待着内存里,超过3.2G的话就会有后台进程来清理。
  • vm.dirty_ratio是可以用脏数据填充的绝对最大系统内存量,当内存脏数据量到达此点时,必须将所有脏数据提交到磁盘,同时所有新的bufferedI/O块都会被阻塞,直到脏数据被写入磁盘。这通常是长I/O卡顿的原因,但这也是保证内存中不会存在过量脏数据的保护机制。
  • 参数一与二的另外一种写法:vm.dirty_background_bytesvm.dirty_bytes是另一种指定这些参数的方法。如果设置_bytes版本,则_ratio版本将变为0,反之亦然。
  • vm.dirty_writeback_centisecs 指定多长时间 pdflush/flush/kdmflush 这些进程会唤醒一次,然后检查是否有缓存需要清理。
  • vm.dirty_expire_centisecs 指定脏数据能存活的时间。在这里它的值是30秒。当 pdflush/flush/kdmflush 在运行的时候,他们会检查是否有数据超过这个时限,如果超过则会把它异步地写到磁盘中。毕竟数据在内存里待太久也会有丢失风险。
  • dirtytime_expire_seconds:默认值12 60 60,即12小时。一个inode在12小时内未存在任何更新,则很可能其时间戳在过去的12小时内存在过变更,比如lazy方式更新。因此系统默认以12小时为周期的强制查看inode是否存在时间戳变更,避免文件时间戳更新过于迟滞。

如何理解上述参数呢???

脏数据占用比例=等于 dirty pages 的内存 / 节点可用内存 *100%,下面简称A

ps:
可用内存可以理解为"free" 命令输出的"available" 内存。

我们可以把整个buffer空间当成一个水桶
/proc/sys/vm/dirty_background_ratio如果为50,代表水盛放到一个半,就立刻开闸放水,但是但是但是,需要注意的是
你在放水的时候,同时可能有水进入,而注水的速度是有可能超过放水的速度
所以有可能出现的一个现象就是,A值超过了dirty_background_ratio值,linux内核开始不停地放水,放水的目的是为了让A降下来,但是因为
注水速度大于放水速度,而出现,A不降反增

能让它增满吗???肯定不能,这就需要有另外一个参数来限制,如果注水的速度实在太离谱了,那你就别注,你原地停止,可别真别把内存吃满了
等放水放到安全线你再继续注水

这就是dirty_radio,控制最大水位,一旦达到这个最大水位,所有的buffer I/O写文件操作都会被阻塞住,直到它写的数据页都写入磁盘为止

有没有可能A永远没有超过dirty_background_ratio ,也没有超过dirty_ratio,那么脏数据就不往硬盘刷了吗,肯定不行,断电数据就没了啊,而且长期不刷是要闹哪样,写完了之后很长一段时间用户却发现磁盘没有这可不行
这就需要用到下面的内核参数,固定时间来刷新

这就需要用到参数dirty_writeback_centisecs:默认值为500,单位为1/100秒,所以默认代表5秒唤醒内核的flush线程来处理dirty pages

另外,为了防止一些脏数据在内存中停留时间过久,就需要用到内核参数dirty_expire_centisecs:与上面一样都是以百分之一秒为单位,所以默认为30s,指的是dirty page 在内存中存放的最长时间,如果一个 dirty page 超过这里定义的时间,那么内核的 flush 线程也会把这个页面写入磁盘

2.3 查看脏数据

联系管理员微信tutu19192010,注册账号

上一篇
下一篇
Copyright © 2022 Egon的知识星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术