转自: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. HDU 4193 Non-negative Partial Sums(单调队列)

     题目大意: 给定一个长度为n的循环序列.从n个不同位置開始,问有几个位置使得一下情况成立:全部前缀的和都大等于0(n <=1000000). 下午的训练赛.之前没学过单调队列所以用的线段树 ...

  2. Ubuntu出现Authentication failure(认证失败)的解决方法(转)

    当我们想在刚安装的Linux系统启动某些服务或者想进入root用户时提示认证失败或者权限不够时,原因是刚安装Ubuntu后,root用户默认是未激活的,不允许登录,也不允许使用su命令到转到root用 ...

  3. windows下python调用c文件流程

    1.新建fun.c文件和fun.h文件 #include <stdio.h> #include <stdlib.h> #include <string.h> int ...

  4. set去重,session,cookie c#与python 对比

    端口,发送请求进行监听,然后处理 session 是存储在服务器端的数据,靠sessionId来验证获取信息,没有大小和类型限制, cookie   是存储在客户端的数据,可以长期使用,有面临被获取的 ...

  5. Python进阶(5)_进程与线程之协程、I/O模型

    三.协程 3.1协程概念 协程:又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存 ...

  6. POJ 3468 A Simple Problem with Integers 【线段树】

    题目链接 http://poj.org/problem?id=3468 思路 线段树 区间更新 模板题 在赋初始值的时候,按点更新区间就可以 AC代码 #include <cstdio> ...

  7. OpenGL学习进程(8)第六课:点、边和图形(三)绘制图形

    本节是OpenGL学习的第六个课时,下面介绍OpenGL图形的相关知识:     (1)多边形的概念: 多边形是由多条线段首尾相连而形成的闭合区域.OpenGL规定,一个多边形必须是一个“凸多边形”. ...

  8. SpringBoot2.1.0 application.properties配置

    # =================================================================== # COMMON SPRING BOOT PROPERTIE ...

  9. C#无边框窗体移动的三种方法

    1. 重写WndProc protected override void WndProc(ref Message m) { const int WM_NCHITTEST = 0x84; const i ...

  10. PMON使用手册

    转:http://www.docin.com/p-1949877603.html