转载

QEMU-KVM 和 Ceph RBD 的 缓存机制总结

QEMU-KVM 的缓存机制的概念很多,Linux/KVM I/O 软件栈的层次也很多,网上介绍其缓存机制的文章很多。边学习变总结一下。本文结合 Ceph 在 QEMU/KVM 虚机中的使用,总结一下两者结合时缓存的各种选项和原理。

1. QEMU/KVM 缓存机制

先以客户机(Guest OS) 中的应用写本地磁盘为例进行介绍。客户机的本地磁盘,其实是 KVM 主机上的一个镜像文件虚拟出来的,因此,客户机中的应用写其本地磁盘,其实就是写到KVM主机的本地文件内,这些文件是保存在 KVM 主机本地磁盘上。

先来看看 I/O 协议栈的层次和各层次上的缓存情况。

1.1 Linux 内核中的缓存

熟悉 Linux Kernel 的人都知道在内核的存储体系中主要有两种缓存,一是 Page Cache,二是 Buffer Cache。Page Cache 是在 Linux IO 栈中为文件系统服务的缓存,而 Buffer Cache 是处于更下层的 Block Device 层,由于应用大部分使用的存储数据都是基于文件系统,因此 Buffer Cache 实际上只是引用了 Page Cache 的数据,而只有在直接使用块设备跳过文件系统时,Buffer Cache 才真正掌握缓存。关于 Page Cache 和 Buffer Cache 更多的讨论参加 What is the major difference between the buffer cache and the page cache? 。

这些 Cache 都由内核中专门的数据回写线程负责来刷新到块设备中,应用可以使用如 fsync(2), fdatasync(2) 之类的系统调用来完成强制执行对某个文件数据的回写。像数据一致性要求高的应用如 MySQL 这类数据库服务通常有自己的日志用来保证事务的原子性,日志的数据被要求每次事务完成前通过 fsync(2) 这类系统调用强制写到块设备上,否则可能在系统崩溃后造成数据的不一致。而 fsync(2) 的实现取决于文件系统,文件系统会将要求数据从缓存中强制写到持久设备中。

1.2 I/O 协议栈层次及缓存

QEMU-KVM 和 Ceph RBD 的 缓存机制总结

( 来源 )

主要组成部分:

  • Guest OS 和 HOST OS Page Cache

Page Cache 是客户机和主机操作系统维护的用来提高存储 I/O 性能的缓存,它是 Linux 虚拟文件系统缓存的一部分,位于操作系统内存中,它是易失性的,因此,在操作系统奔溃或者系统掉电时,这些数据会消失。数据是否写入 Page cache 可以被控制。当会写入 page cache 时,当数据被写入 page cache 后,应用就认为写入完成了,随后的读操作也会从 page cache 中读取数据,这样性能会提高。可以使用 fsync 来将数据从 page cache 中拷贝到持久存储。

在 KVM 环境中,host os 和 guest os 都有 page cache,因此,最好是能绕过一个来提高性能。

    • 如果 guest os 中的应用使用 direct I/O 方式,guest os 中 page cache 会被绕过。
    • 如果 guest os 使用 no cache 方式,host os 的 page cache 会被绕过。

该缓存的特点是读的时候,操作系统先检查页缓存里面是否有需要的数据,如果没有就从设备读取,返回给用户的同时,加到缓存一份;写的时候,直接写到缓存去,再由后台的进程定期涮到磁盘去。这样的机制看起来非常的好,在实践中也效果很好。

考虑到其易失性,需要考虑它的大小,特别是在 KVM 主机上。现在 KVM 主机的内存可以很大。其内存越大, 那么在 Page cache 中还没有 flush 到磁盘(虚拟或者物理的)的脏数据就越多,其丢失的后果就越严重。默认的话,Linux 2.6.32 在脏数据达到内存的 10% 的时候会自动开始 flush。

  • Guest Disk (virtual disk device):客户机虚机磁盘设备
  • QEMU image:QEMU 镜像文件
  • Physical disk cache

这是磁盘的 write cache,它会提高数据到存储的写性能。写到 disk write cache 后,写操作会被认为完成了,即使数据还没真正被写入物理磁盘。这样,如果 disk write cache 没有备份电池的话,断电将导致尚未写入物理磁盘的数据丢失。要强制数据被写入磁盘,应用可以通过操作系统可以发出 fsync 命令。因此,disk write cache 会提到写I/O 性能,但是,需要确保应用和存储栈会将数据写入磁盘中。如果 disk write cache 被关闭,那么写性能将下降,但是断电时数据丢失将会避免。

  • Physical disk platter:物理磁盘

1.3 客户机磁盘(drive)的缓存模式

在 libvirt xml 中使用 'cache' 参数来指定driver的缓存模式,比如:

 <disk type='file' device='disk'>             <driver name='qemu' type='raw' cache='writeback'/> 

QEMU/KVM 支持如下这些缓存模式作为 ‘cache’ 的可选值:

缓存模式 说明 GUEST OS Page cache Host OS Page cache Disk write cache 数据安全性
Cache=none 客户机的I/O 不会被缓存到 host OS page cache,而是会放在 disk write cache。
在大 I/O 需求时使用这种模式
基本上这是最优模式,而且是支持实时迁移的唯一模式
Enable Disable Enable 不安全. Only ensured when cache is battery-backed  or fsync
cache = unsafe 跟 writeback 类似,只是会忽略 GUEST OS 的 flush 操作,完全由 HOST OS 控制 flush E E E 最不安全,只有在特定的场合才会使用
Cache=writeback I/O 写到 HOST OS Page cache 就算成功,支持 GUEST OS flush 操作。效率最快,但是也最不安全 E E E 不安全. (only for temporary data where potential data loss is not a concern  )
Cache=writethrough I/O 会被缓存在host OS page cache 便于以后的读,但是它会被写入物理存储才算成功。
这是较慢的模式。
最好是用在小规模的有低 I/O 需求客户机的场景中
当不需要支持实时迁移时,如果不支持writeback 则可用
E E D 安全
Cache=DirectSync 跟 writethrough 类似,只是不写入 HOST OS Page cache E D D 同 ”O_SYNC“,对一些数据库应用来说,往往会直接使用这种模式,直接将数据写到数据盘
cache=default

使用各种driver 类型的默认cache 模式

qcow2:默认 writeback

  • 性能:  writeback = unsafe > none > writethrough = directsync
  • 安全性: writethrough = directsync > none > writeback > unsafe

看看性能比较:

QEMU-KVM 和 Ceph RBD 的 缓存机制总结

基本结论:

  • 各种模式的性能差别非常大
  • 对于数据库这样的应用,使用 directsync 模式,数据直接写入物理磁盘才算成功
  • 对于重要的数据或者小 I/O 的场景,使用 writethrough
  • 对于一般的应用,或者大 I/O 场景,使用 none。这个可以说是大部分情况下的最优选项。
  • 对于丢失了也无所谓的数据,可以使用 writeback

1.4 KVM Write barrier

1.4.1 什么是 KVM write barrier

上面的基本结论中,writethrough 是最安全的,但是效率也是最低的。它将数据放在 HOST Page Cache 中,一方面来支持读缓存,另一方面,在每一个 write 操作后,都执行 fsync,确保数据被写入物理存储。只有在数据被写入磁盘后,写操作才会标记为成功。这种模式下,客户机的 virtual storage adapter 会被通知不会使用 writeback 模式,因此,它不会主动发送 fsync 命令,因为它是重复的,不需要的。

那还有没有什么办法使它在保持数据可靠性的同时,使它的效率提高一些呢?答案是 KVM Write barrier 功能。新的 KVM 版本中,启用了 “barrier-passing” 功能,它能保证在不管是用什么缓存模式下,将客户机上应用写入的数据 100% 写入持久存储。

好吧,这真是个神器。。那它是如何实现的呢?以 fio 工具为例,在支持 write barrier 的客户机操作系统上,在使用 direct 和 sync 参数的情况下,会使用这种模式。它在写入部分数据以后,会使得操作系统发出一个 fdatasync 命令,这样 QEMU-KVM 就会将缓存中的数据 flush 到物理磁盘上。

基本过程:

  1. 在一个会话中写入数据
  2. 发出 barrier request
  3. 会话中的所有数据被 flush 到物理磁盘
  4. 继续下一个会话

看起来和 writethrough 差不多是吧。但是它的效率比 writethrough 高。两者的区别在于,writethrough 是每次 write 都会发 fsync,而 barrier-passing 是在若干个写操作或者一个会话之后发 fdatasync 命令,因此其效率更高。

也可以看到,使用它是有条件的:

  • KVM 版本较新
  • 客户机操作系统支持:在较新的 Linux 发行版中都会支持
  • 客户机中的文件系统支持 barrier (ext4 支持并默认开启;ext3 支持但默认不开启),而且整个 I/O 协议栈中的各个层次都支持 flush 操作
  • 应用需要在需要的时候发出 flush 指令。

也可以看到,应用在需要的时候发出 flush 指令是关键。一方面,Cache 都由内核中专门的数据回写线程负责来刷新到块设备中;另一方面,应用可以使用如 fsync(2), fdatasync(2) 之类的系统调用来完成强制执行对某个文件数据的回写。像数据一致性要求高的应用如 MySQL 这类数据库服务通常有自己的日志用来保证事务的原子性,日志的数据被要求每次事务完成前通过 fsync(2) 这类系统调用强制写到块设备上,否则可能在系统崩溃后造成数据的不一致。而 fsync(2) 的实现取决于文件系统,文件系统会将要求数据从缓存中强制写到持久设备中。类似地,支持 librbd 的QEMU 在适当的时候也会发出 flush 指令。

1.4.2 KVM write barrier 和 KVM 缓存模式的结合

考虑到 KVM write barrier 的原理和 KVM 各种缓存模式的原理,显而易见,writeback + barrier 的方式下,可以实现 效率最高+数据安全 这种最优效果。

QEMU-KVM 和 Ceph RBD 的 缓存机制总结

1.5 小结

  • 在客户机可以启用 write barrier 时,使用 write-back or nocache + barrier,然后应用会在合适的时候发出 flush 指令。
  • 在客户机不支持 write barrier 时,如果对读敏感应用,使用 write-back (可以使用 pagecache);对需要同步数据的应用,使用 noncache;最安全的情况下,使用 writethrough。
  • 对于一些能过备用电池或者别的技术(比如设备上有电容等)保证了在掉电情况下数据也不会丢失的情况下,barrier 最好被禁止。比如企业存储的Adatper,或者 SSD。
    • ”If the device does not need cache flushes it should not report requiring flushes, in which case nobarrier will be a noop.“
    • ”With a RAID controller with battery backed controller cache and cache in write back mode, you should turn off barriers - they are unnecessary in this case, and if the controller honors the cache flushes, it will be harmful to performance. But then you *must* disable the individual hard disk write cache in order to ensure to keep the filesystem intact after a power failure.“。
    • 一个例子是,Ceph OSD 节点上的 SSD 分区,一般都使用 ”nobarrier“参数 来禁用 barrier。 

2. RBDCache

RBDCache 是 Ceph 的块存储接口实现库 Librbd 用来在客户端侧缓存数据的目的,它主要提供了 读数据 缓存, 写数据汇聚写回 的目的,用来提高顺序读写的性能。需要说明的是,Ceph 既支持以内核模块的方式来实现对 Linux 动态增加块设备,也支持以 QEMU Block Driver 的形式给使用 QEMU 虚拟机增加虚拟块设备,而且两者使用不同的库,前者是内核模块的形式,后者是普通的用户态库,本文讨论的 RBDCache 针对后者,前者使用内核的 Page Cache 达到目的。

2.1 librbd I/O 协议栈

QEMU-KVM 和 Ceph RBD 的 缓存机制总结

从这个栈可以看出来,RBDCache 类似于磁盘的 write cache。它应该有三个功能:

  • 写缓存:开启时,librdb 将数据写入 RBDCache,然后在被 flush 到 Ceph 集群,其效果就是多个写操作被合并,但是有一定的时间延迟。
  • 读缓存:数据会在缓存中被保留一段时间,这期间的 librbd 读数据的话,会直接从缓存中读取,提高读效率。
  • 合并写操作:对同一个 OSD 上的多个写操作,应该会合并为一个大的写操作,提高写入效率。 ”Due to several objects map to the same physical disks, the original logical sequential IO streams mix together (green, orange, blue and read blocks).   来源 “

因此,需要注意的是,理论上,RBDCache 对顺序写的效率提升应该非常有帮助,而对随机写的效率提升应该没那么大,其原因应该是后者合并写操作的效率没前者高(也就是能够合并的写操作的百分比比较少)。具体效果待测试。

在使用 QEMU 实现的 VM 来使用 RBD 块设备,那么 Linux Kernel 中的块设备驱动是 virtio_blk,它会对块设备各种请求封装成一个消息通过 virtio 框架提供的队列发送到 QEMU 的 IO 线程,QEMU 收到请求后会转给相应的 QEMU Block Driver 来完成请求。当 QEMU Block Driver 是 RBD 时,缓存就会交给 Librbd 自身去维护,也就是一直所说的 RBDCache;用户在使用本地文件或者 Host 提供的 LVM 分区时,跟 RBDCache 同样性质的缓存包括了 Guest Cache 和 Host Page Cache,见本文第一部分的描述。

2.2 RBDCache 的原理

2.2.1 RBDCache 的配置

在 ceph.conf 中,设置 rbd  cache = true 即可以启用 RBDCache。它有以下几个主要的配置参数:

配置项 含义 默认值
rbd cache 是否启用 RBDCache true,启用
rbd_cache_size Librbd 能使用的最大缓存大小 32 MiB
rbd_cache_max_dirty 缓存中允许脏数据的最大值,用来控制回写大小,不能超过 rbd_cache_size 24 MiB
rbd_cache_target_dirty 开始执行回写过程的脏数据大小,不能超过 rbd_cache_max_dirty 16MiB
rbd_cache_max_dirty_age 缓存中单个脏数据最大的存在时间,避免可能的脏数据因为迟迟未达到开始回写的要求而长时间存在 1 秒

可见,默认情况下:

  • 在主机操作系统内内存内会分配 32MiB 的空间用于 RBD 做缓存使用
  • 允许最大的脏数据大小为 24MiB,超过的话,可能会阻止继续写入(需要确认)
  • 在脏数据总共有 16MiB 时,开始回写过程,将数据写入Ceph集群
  • 在单个脏数据(目前在 Librbd 用户态库中主要以 Object Buffer Extent 为基本单位进行缓存,这里的粒度应该是 Object Buffer Extent)存在超过 1 秒时,对它启用回写

也能看出,RBDCache 从空间和时间来方面,在效率和数据有效性之间做平衡。

2.2.2 缓存中的数据被 flush 到 Ceph cluster

有两种类型的 flush:

  • RBD 主动的,在 RBDCache 规定的空间或者数据保存时间达到阈值之后,会触发回写
  • RBD 被动的,librbd 的 flush 接口被调用,全部缓存中的数据也会被回写。又可以细分为两种类型:
    • QEMU 在合适的时候会自动发出 flush:QEMU 作为最终使用 Librbd 中 RBDCache 的用户,它在 VM 关闭、QEMU 支持的热迁移操作或者 RBD 块设备卸载时都会调用 QEMU Block Driver 的 Flush 接口,确保数据不会被丢失。因此,此时,需要用户在使用了开启 RBDCache 的 RBD 块设备 VM 时需要给 QEMU 传入 “cache=writeback” 确保 QEMU 知晓有缓存的存在,不然 QEMU 会认为后端并没有缓存而选择将 Flush Request 忽略。
    • 应用发出 flush,比如 fio,可以设置 fdatasync 为一个大于零的整数,从而在若干次写操作后执行fdatasync。( fdatasync=int Like fsync, but uses fdatasync(2) instead to only sync the data parts of the file. Default: 0“

关于第二种 flush,这里的一个问题是,什么时候会有这种主动 flush 指定发出。有问题说,”QEMU 作为最终使用 Librbd 中 RBDCache 的用户,它在 VM 关闭、QEMU 支持的热迁移操作或者 RBD 块设备卸载时都会调用 QEMU Block Driver 的 Flush 接口“。同时,一些对数据的安全性敏感的应用也可以通过操作系统在需要的时候发出 flush 指定,比如一些数据库系统,你可以使用 fio 工具的 fdatasync 参数在指定的写入操作后发出 fdatasync 指令。具体效果还待测试。

librados 的 flush API:

 CEPH_RADOS_API int rados_aio_flush(rados_ioctx_t io) Block until all pending writes in an io context are safe  This is not equivalent to calling rados_aio_wait_for_safe() on all write completions, since this waits for the associated callbacks to complete as well.  Parameters io - the context to flush 

2.2.3 RBDCache 中数据的易失性

因为 RBDCache 是利用内存来缓存数据,因此数据也是易失性的。那么,最安全的是,设置 rbd_cache_max_dirty = 0,就是不缓存数据,相当于 writethrough 的效果。很明显,这没有实现 RBDCache 的目的。

另外,Ceph 还提供 rbd_cache_writethrough_until_flush 选项,它使得 RBDCache 在收到第一个 flush 指令之前,使用 writethrough 模式,透传数据,避免数据丢失;在收到第一个 flush 指令后,开始 writeback 模式,通过 KVM barrier 功能来保证数据的可靠性。

2.3 小结

各种配置下的Ceph RBD 缓存效果:

配置 rbd_cache_writethrough_until_flush 的值 缓存效果
rbd cache = false N/A 没有读写缓存,等同于 directsync

rbd cache = true

rbd_cache_max_dirty = 0

N/A 只有读缓存,没有写缓存,等同于 writethrough

rbd cache = true

rbd_cache_max_dirty > 0

“cache=writeback”

True

在收到 QEMU 发出的第一个 flush 前,

使用 writethrough 模式;收到后,使用 writeback 模式

rbd cache = true

rbd_cache_max_dirty > 0

“cache=writeback”

False 一直使用 writeback 模式,QEMU 会在特定时候发出 flush,可能会导致数据丢失

rbd cache = true

rbd_cache_max_dirty > 0

“cache=none”

True 一直使用 writethrough 模式,没有写缓存,只有读缓存

rbd cache = true

rbd_cache_max_dirty > 0

“cache=writeback”

False 一直使用 writeback 模式,QEMU 会发出 flush 使缓存数据写入Ceph 集群

参考资料:

解析Ceph: RBDCache 背后的世界

http://docs.ceph.com/docs/hammer/rbd/rbd-config-ref/

KVM storage performance and cache settings on Red Hat Enterprise Linux 6.2

http://linux.die.net/man/1/fio

http://xfs.org/index.php/XFS_FAQ#Write_barrier_support.

https://libvirt.org/formatdomain.html

https://www.quora.com/What-is-the-major-difference-between-the-buffer-cache-and-the-page-cache

正文到此结束
Loading...