转自:https://www.ustack.com/blog/ceph-internal-rbdcache/

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

RBDCache 的实现

RBDCache 目前在 Librbd(以下统指用户态库)中主要以 Object Buffer Extent 为基本单位进行缓存,一个 RBD 块设备在 Lirbd 层会以固定大小分为若干个对象,而读写请求通常会有不同的 IO 大小,每个请求的 Buffer 大小都会以 Object 为单位放到一个或多个 Object Buffer Extent 中。目前 RBDCache 只支持以内存的形式存在,因此需要提供一些策略来不断回写到 Ceph 集群来实现持久化。在 Lirbd 中有若干选项来控制 RBDCache 的大小和回写策略:

  • rbd_cache_size: Librbd 能使用的最大缓存大小
  • rbd_cache_max_dirty: 缓存中允许脏数据的最大值,用来控制回写大小,不能超过 rbd_cache_size
  • rbd_cache_target_dirty: 开始执行回写过程的脏数据大小,不能超过 rbd_cache_max_dirty
  • rbd_cache_max_dirty_age: 缓存中单个脏数据最大的存在时间,避免可能的脏数据因为迟迟未达到开始回写的要求而长时间存在

除了当满足缓存回写要求大小或者时间才会回写数据外,Librbd 提供的 Flush 接口同样能将缓存中的脏数据全部回写。

RBDCache 由于只是以内存的形式存在,因此大部分人可能会关心是否由于意外的 Kernel Crash 或者 Host 端掉电而导致潜在的数据丢失情况,那么下面就主要讨论这种情况。

Cache 在内核

熟悉 Linux Kernel 的人都知道在内核的存储体系中主要有两种缓存,一是 Page Cache,二是 Buffer Cache。Page Cache 是在 Linux IO 栈中为文件系统服务的缓存,而 Buffer Cache 是处于更下层的 Block Device 层,由于应用大部分的使用存储数据是基于文件系统,因此 Buffer Cache 实际上只是引用了 Page Cache 的数据,而只有在直接使用块设备跳过文件系统时,Page 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) 的实现取决于文件系统,文件系统会将要求数据从缓存中强制写到持久设备中。但是这里还有另外一个大“麻烦”,通常成为 Block Device Cache(块设备缓存),这类缓存并不存在归 Kernel 管理,它或许是传统磁盘上的控制器缓存,RAID 控制器缓存或者就像本文提到的 RBDCache,它主要是被块设备自己管理。

块设备缓存

传统硬件块设备提供缓存的目的与 RBDCache 的意义是一致的,它们同样面临在机器掉电情况下,存在于磁盘控制器上的缓存丢失的情况。但是现代磁盘控制器或者 RAID 卡都会配置一个小型电容用来实现在机器掉电后对缓存数据的回写,但是 Linux Kernel 无法知晓到底是否存在这类“急救”装置来实现持久性,因此,大多数文件系统在实现 fsync 这类接口时,同时会使用 Kernel Block 模块提供的 “blkdev_issue_flush” API 去给块设备发送一个 Flush Request,块设备收到 Flush Request 后就会回写自身的缓存。但是如果机器上存在电容,那么实际上 Flush Request 会大大降低文件系统的读写性能,因此,文件系统会提供如 barrier 选项来让用户选择是否需要发送 Flush Request,比如 XFS 在 mount 时就支持 “barrier=0″ 来选择不发送 Flush Request (Write barrier support.)。

相关的文件系统对持久化的 Trick 参考深入文件的读与写—文件系统保证陷阱

QEMU 中的缓存

回到 RBDCache 的使用情况里,用户往往是使用 QEMU 实现的 VM 来使用 RBD 块设备,那么 Linux Kernel 中的块设备驱动是 virtio_blk。它会对块设备各种请求封装成一个消息通过 virtio 框架提供的队列发送到 QEMU 的 IO 线程,QEMU 收到请求后会转给相应的 QEMU Block Driver 来完成请求。用户在使用本地文件或者 Host 提供的 LVM 分区时,跟 RBDCache 同样性质的缓存包括了 Guest Cache 和 Host Page Cache,在本文暂且不提这种情况下的缓存,相关信息参考KVM storage performance and cache settings on Red Hat Enterprise Linux 6.2

而当 QEMU Block Driver 是 RBD 时,缓存就会交给 Librbd 自身去维护,也就是一直所说的 RBDCache。用户在使用了开启 RBDCache 的 RBD 块设备 VM 时需要给 QEMU 传入 “cache=writeback” 确保 QEMU 知晓有缓存的存在,不然 QEMU 会认为后端并没有缓存而选择将 Flush Request 忽略。

QEMU 作为最终使用 Librbd 中 RBDCache 的用户,它在 VM 关闭、QEMU 支持的热迁移操作或者 RBD 块设备卸载时都会调用 QEMU Block Driver 的 Flush 接口。

RBDCache 可能造成的数据破坏

通过上面的梳理,可以发现开启 RBDCache 的 RBD 块设备实际上就是一个不带电容的磁盘,我们需要让文件系统开启 barrier 模式,幸运的是,这也是文件系统的默认情况。除此之外,因为文件系统实际上可能管理的是通过 LVM 这种逻辑卷管理工具得到的分区,因此必须确保文件系统下面的 Linux Device Mapping 层也能够支持 Flush Request,LVM 在较早版本的 Kernel 中就已经支持 Flush Request,而其他 DM-* 模块可能就会忽略该请求,这就需要用户非常明确的了解。

幸运的是,rbd 会默认开启一个叫”rbd_cache_writethrough_until_flush”的一个选项,它的作用就是为了避免一些不支持 “flush” 的 VM 来使用 RBDCache,它的主要方式是在用户开启 RBDCache 的情况下,在收到来自 VM 的第一个 Flush 请求前,它是不会在逻辑上启用 Cache 的。这样就避免了旧内核不支持 Flush 的问题。

解析Ceph: RBDCache 背后的世界的更多相关文章

  1. 解析CEPH: 存储引擎实现之一 filestore

    Ceph作为一个高可用和强一致性的软件定义存储实现,去使用它非常重要的就是了解其内部的IO路径和存储实现.这篇文章主要介绍在IO路径中最底层的ObjectStore的实现之一FileStore. Ob ...

  2. [源码解析] Flink UDAF 背后做了什么

    [源码解析] Flink UDAF 背后做了什么 目录 [源码解析] Flink UDAF 背后做了什么 0x00 摘要 0x01 概念 1.1 概念 1.2 疑问 1.3 UDAF示例代码 0x02 ...

  3. 解析Ceph: Snapshot

    经常有开发者在邮件列表中会问到Ceph Snapshot的实现方式,受限于目前有限的实现文档和复杂的代码结构和代码量,弄清楚Ceph Snapshot并不是一件容易的事.正好最近在重构Ceph存储引擎 ...

  4. 解析c语言背后的汇编代码

    源码 很简单的c语言代码,作用是交换两个数: #include <stdio.h> void swap(int * a, int * b) { *a = *a + *b - (*b = * ...

  5. 解析 Ceph: FileJournal 的作用

      很多的用户在提到 Ceph 性能的时候都会提到“写放大”这点,实际上就是 FileJournal 在起作用.只要使用默认的 FileStore,所有数据包括 metadata 都会在 FileJo ...

  6. 解析Ceph: 数据的端到端正确性和 Scrub 机制

    转自:https://www.ustack.com/blog/ceph-internal-scrub/ Ceph 的主要一大特点是强一致性,这里主要指端到端的一致性.众所周知,传统存储路径上从应用层到 ...

  7. 解析Ceph: 恢复与数据一致性

    转自:https://www.ustack.com/blog/ceph-internal-recovery-and-consistency/ 作为一个面向大规模的分布式存储系统,故障处理是作为一个常态 ...

  8. 深度解析双十一背后的阿里云 Redis 服务

    摘要: Redis是一个使用范围很广的NOSQL数据库,阿里云Redis同时在公有云和阿里集团内部进行服务,本文介绍了阿里云Redis双11的一些业务场景:微淘社区之亿级关系链存储.天猫直播之评论商品 ...

  9. 理解 QEMU/KVM 和 Ceph(1):QEMU-KVM 和 Ceph RBD 的 缓存机制总结

    本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合: (1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结 (2)QEMU 的 RBD 块驱动(block driver) (3)存 ...

随机推荐

  1. Django 请求生命周期【图示】

    Django 请求生命周期

  2. python并发编程&多进程(一)

    本篇理论居多,实际操作见:  python并发编程&多进程(二) 一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行) ...

  3. MySql存储过程、函数

    存储过程和函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句.存储过程和函数可以避免开发人员重复的编写相同的SQL语句.而且,存储过程和函数是在MyS ...

  4. from PyQt4.QtGui import * 提示 ImportError: DLL load failed: %1 is not a valid Win32 application.

    个人用64位电脑安装了64位的PyQt后 from PyQt4.QtGui import * 提示 ImportError: DLL load failed: %1 is not a valid Wi ...

  5. 剑指offer 面试24题

    面试24题: 题目:反转链表 题:输入一个链表,反转链表并输出反转后链表的头节点. 解题思路:注意反转时出现断裂现象,定义3个指针,分别指向当前遍历到的节点pNode.它的前一个节点pPrev及后一个 ...

  6. 剑指offer 面试28题

    面试28题: 题目:对称的二叉树题: 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的 解题思路: 可以定义一种遍历算法,先遍历右子节点再遍 ...

  7. grads 读取shp

    自从GrADS2.0.a8版本开始,GrADS引入了对shp图形的支持,关于此格式在这里不多说, 于是今晚就简单测试了一下最简单画图和查询命令(后续还将测试输出shp图形的命令)    测试数据采用的 ...

  8. Android开发BUG及解决方法1

    错误描述: 问题1: Error:Execution failed for task ':app:transformClassesWithDexForDebug'. > com.Android. ...

  9. awk的输出格式控制:print 和printf

    1.两个函数和若干个内部变量控制awk的输出格式: 两个函数:print和printf 内部变量:OFS:输出的列间隔符,默认为tab;  ORS:输出的行间隔符,默认为\n printf更加自由化, ...

  10. INDEL的重新比对和碱基质量分数的重新校准

    1.为什么要做这两步(why): indel的重新比对:这是由于比对软件的自身限制,其可能将包括indel的read解释为snp的read,这就导致calling的错误和后面的碱基质量分数的重新校准. ...