# Ceph的结构

### Ceph存储系统的逻辑层次结构如下图所示：

![K92EuR.png](https://figure-bed-1304788733.cos.ap-guangzhou.myqcloud.com/typora/202206091405568.png)

自下向上，可以将Ceph系统分为四个层次：

### （1）基础存储系统RADOS

（Reliable, Autonomic, Distributed Object Store，即可靠的、自动化的、分布式的对象存储）

顾名思义，这一层本身就是一个完整的对象存储系统，所有存储在Ceph系统中的用户数据事实上最终都是由这一层来存储的。而Ceph的高可靠、高可扩展、高性能、高自动化等等特性本质上也是由这一层所提供的。因此，理解RADOS是理解Ceph的基础与关键。

物理上，RADOS由大量的存储设备节点组层，每个节点拥有自己的硬件资源（CPU、内存、硬盘、网络），并运行着操作系统和文件系统。4.2、4.3节将对RADOS进行展开介绍。

### （2）基础库librados

这一层的功能是对RADOS进行抽象和封装，并向上层提供API，以便直接基于RADOS（而不是整个Ceph）进行应用开发。特别要注意的是，RADOS是一个对象存储系统，因此，librados实现的API也只是针对对象存储功能的。

RADOS采用C++开发，所提供的原生librados API包括C和C++两种，其文档参见[2]。物理上，librados和基于其上开发的应用位于同一台机器，因而也被称为本地API。应用调用本机上的librados API，再由后者通过socket与RADOS集群中的节点通信并完成各种操作。

### （3）高层应用接口

这一层包括了三个部分：RADOSGW（RADOS Gateway）、 RBD（Reliable Block Device）和Ceph FS（Ceph File System），其作用是在librados库的基础上提供抽象层次更高、更便于应用或客户端使用的上层接口。

其中，RADOS GW是一个提供与Amazon S3和Swift兼容的RESTful API的gateway，以供相应的对象存储应用开发使用。RADOS GW提供的API抽象层次更高，但功能则不如librados强大。因此，开发者应针对自己的需求选择使用。

RBD则提供了一个标准的块设备接口，常用于在虚拟化的场景下为虚拟机创建volume。目前，Red Hat已经将RBD驱动集成在KVM/QEMU中，以提高虚拟机访问性能。

Ceph FS是一个POSIX兼容的分布式文件系统。由于还处在开发状态，因而Ceph官网并不推荐将其用于生产环境中。

### （4）应用层

这一层就是不同场景下对于Ceph各个应用接口的各种应用方式，例如基于librados直接开发的对象存储应用，基于RADOS GW开发的对象存储应用，基于RBD实现的云硬盘等等。

在上文的介绍中，有一个地方可能容易引起困惑：RADOS自身既然已经是一个对象存储系统，并且也可以提供librados API，为何还要再单独开发一个RADOSGW

理解这个问题，事实上有助于理解RADOS的本质，因此有必要在此加以分析。粗看起来，librados和RADOS GW的区别在于，librados提供的是本地API，而RADOS GW提供的则是RESTful API，二者的编程模型和实际性能不同。而更进一步说，则和这两个不同抽象层次的目标应用场景差异有关。换言之，虽然RADOS和S3、Swift同属分布式对象存储系统，但RADOS提供的功能更为基础、也更为丰富。这一点可以通过对比看出。

由于Swift和S3支持的API功能近似，这里以S3举例说明。S3提供的API功能主要包括：

**用户管理操作：** 用户认证、获取账户信息、列出容器列表等；

**容器管理操作：** 创建/删除容器、读取容器信息、列出容器内对象列表等；

**对象管理操作：** 对象的写入、读取、复制、更新、删除、访问许可设置、元数据读取或更新等。

由此可见，S3（以及Swift）提供的API所操作的“对象”只有三个：用户账户、用户存储数据对象的容器、数据对象。并且，所有的操作均不涉及存储系统 的底层硬件或系统信息。不难看出，这样的API设计完全是针对对象存储应用开发者和对象存储应用用户的，并且假定其开发者和用户关心的内容更偏重于账户和数据的管理，而对底层存储系统细节不感兴趣，更不关心效率、性能等方面的深入优化。

而librados API的设计思想则与此完全不同。一方面，librados中没有账户、容器这样的高层概念；另一方面，librados API向开发者开放了大量的RADOS状态信息与配置参数，允许开发者对RADOS系统以及其中存储的对象的状态进行观察，并强有力地对系统存储策略进行控制。换言之，通过调用librados API，应用不仅能够实现对数据对象的操作，还能够实现对RADOS系统的管理和配置。这对于S3和Swift的RESTful API设计是不可想像的，也是没有必要的。

基于上述分析对比，不难看出，librados事实上更适合对于系统有着深刻理解，同时对于功能定制扩展和性能深度优化有着强烈需求的高级用户。基于librados的开发可能更适合于在私有Ceph系统上开发专用应用，或者为基于Ceph的公有存储系统开发后台数据管理、处理应用。而RADOS GW则更适合于常见的基于web的对象存储应用开发，例如公有云上的对象存储服务。



## RADOS的逻辑结构

RADOS的系统逻辑结构如下图所示

![K92kv9.jpg](https://figure-bed-1304788733.cos.ap-guangzhou.myqcloud.com/typora/202206091411127.jpeg)

在使用RADOS系统时，大量的客户端程序通过与OSD或者monitor的交互获取cluster map，然后直接在本地进行计算，得出对象的存储位置后，便直接与对应的OSD通信，完成数据的各种操作。

可见，在此过程中，只要保证cluster map不频繁更新，则客户端显然可以不依赖于任何元数据服务器，不进行任何查表操作，便完成数据访问流程。在RADOS的运行过程中，cluster map的更新完全取决于系统的状态变化，而导致这一变化的常见事件只有两种：**OSD出现故障，或者RADOS规模扩大。** 而正常应用场景下，这两种事件发生 的频率显然远远低于客户端对数据进行访问的频率。



## OSD的逻辑结构

根据定义，OSD可以被抽象为两个组成部分，即系统部分和守护进程（OSD deamon）部分。

OSD的系统部分本质上就是一台安装了操作系统和文件系统的计算机，其硬件部分至少包括一个单核的处理器、一定数量的内存、一块硬盘以及一张网卡。

由于这么小规模的x86架构服务器并不实用（事实上也见不到），因而实际应用中通常将多个OSD集中部署在一台更大规模的服务器上。在选择系统配置时，应当 能够保证每个OSD占用一定的计算能力、一定量的内存和一块硬盘。同时，应当保证该服务器具备足够的网络带宽。具体的硬件配置选择可以参考。

在 上述系统平台上，每个OSD拥有一个自己的OSD deamon。这个deamon负责完成OSD的所有逻辑功能，包括与monitor和其他OSD（事实上是其他OSD的deamon）通信以维护更新系 统状态，与其他OSD共同完成数据的存储和维护，与client通信完成各种数据对象操作等等。

如图所示，RADOS集群主要由两种节点组成。一种是为数众多的、负责完成数据存储和维护功能的OSD（Object Storage Device），另一种则是若干个负责完成系统状态检测和维护的monitor。OSD和monitor之间相互传输节点状态信息，共同得出系统的总体工 作状态，并形成一个全局系统状态记录数据结构，即所谓的cluster map。这个数据结构与RADOS提供的特定算法相配合，便实现了Ceph“无需查表，算算就好”的核心机制以及若干优秀特性。



# Ceph的工作原理及流程

本节将对Ceph的工作原理和若干关键工作流程进行扼要介绍。如前所述，由于Ceph的功能实现本质上依托于RADOS，因而，此处的介绍事实上也是针对RADOS进行。对于上层的部分，特别是RADOS GW和RBD，由于现有的文档中（包括Sage的论文中）并未详细介绍，还请读者多多包涵。

首先介绍RADOS中最为核心的、基于计算的对象寻址机制，然后说明对象存取的工作流程，之后介绍RADOS集群维护的工作过程，最后结合Ceph的结构和原理对其技术优势加以回顾和剖析。

## 寻址流程

Ceph系统中的寻址流程如下图所示：

![image-20221110094732113](https://note-1308251438.cos.ap-guangzhou.myqcloud.com/typora/202211100947243.png)

几个概念说明如下：

1. File – 此处的file就是用户需要存储或者访问的文件。对于一个基于Ceph开发的对象存储应用而言，这个file也就对应于应用中的“对象”，也就是用户直接操作的“对象”。
2. Ojbect—— 此处的object是RADOS所看到的“对象”。Object与上面提到的file的区别是，object的最大size由RADOS限定（通常为2MB或4MB），以便实现底层存储的组织管理。因此，当上层应用向RADOS存入size很大的file时，需要将file切分成统一大小的一系列object（最后一个的大小可以不同）进行存储。为避免混淆，在本文中将尽量避免使用中文的“对象”这一名词，而直接使用file或object进行说明。
3. PG（Placement Group）—— 顾名思义，PG的用途是对object的存储进行组织和位置映射。具体而言，一个PG负责组织若干个object（可以为数千个甚至更多），但一个object只能被映射到一个PG中，即，PG和object之间是“一对多”映射关系。同时，一个PG会被映射到n个OSD上，而每个OSD上都会承载大量的PG，即，PG和OSD之间是“多对多”映射关系。在实践当中，n至少为2，如果用于生产环境，则至少为3。一个OSD上的PG则可达到数百个。事实上，PG数量的设置牵扯到数据分布的均匀性问题。关于这一点，下文还将有所展开。
4. OSD—— 即object storage device，前文已经详细介绍，此处不再展开。唯一需要说明的是，OSD的数量事实上也关系到系统的数据分布均匀性，因此其数量不应太少。在实践当中，至少也应该是数十上百个的量级才有助于Ceph系统的设计发挥其应有的优势。
5. Failure domain—— 这个概念在论文中并没有进行定义，好在对分布式存储系统有一定概念的读者应该能够了解其大意。

**基于上述定义，便可以对寻址流程进行解释了。具体而言， Ceph中的寻址至少要经历以下三次映射。**



## 三次映射：

### 1. File -> object映射

这次映射的目的是，将用户要操作的file，映射为RADOS能够处理的object。其映射十分简单，本质上就是按照object的最大size对file进行切分，相当于RAID中的条带化过程。这种切分的好处有二：一是让大小不限的file变成最大size一致、可以被RADOS高效管理的object；二是让对单一file实施的串行处理变为对多个object实施的并行化处理。

每一个切分后产生的object将获得唯一的oid，即object id。其产生方式也是线性映射，极其简单。图中，ino是待操作file的元数据，可以简单理解为该file的唯一id。ono则是由该file切分产生的某个object的序号。而oid就是将这个序号简单连缀在该file id之后得到的。举例而言，如果一个id为filename的file被切分成了三个object，则其object序号依次为0、1和2，而最终得到的oid就依次为filename0、filename1和filename2。

这里隐含的问题是，**ino的唯一性必须得到保证，否则后续映射无法正确进行。**

### 2. Object -> PG映射

在file被映射为一个或多个object之后，就需要将每个object独立地映射到一个PG中去。这个映射过程也很简单，如图中所示，其计算公式是：

```
hash(oid) & mask -> pgid
```

由此可见，其计算由两步组成。首先是使用Ceph系统指定的一个静态哈希函数计算oid的哈希值，将oid映射成为一个近似均匀分布的伪随机值。然后，将这个伪随机值和mask按位相与，得到最终的PG序号（pgid）。根据RADOS的设计，给定PG的总数为m（m应该为2的整数幂），则mask的值为m-1。因此，哈希值计算和按位与操作的整体结果事实上是从所有m个PG中近似均匀地随机选择一个。基于这一机制，当有大量object和大量PG时，RADOS能够保证object和PG之间的近似均匀映射。又因为object是由file切分而来，大部分object的size相同，因而，这一映射最终保证了，各个PG中存储的object的总数据量近似均匀。

从介绍不难看出，这里反复强调了 **“大量”**。只有当object和PG的数量较多时，这种伪随机关系的近似均匀性才能成立，Ceph的数据存储均匀性才有保证。为保证“大量”的成立，**一方面，object的最大size应该被合理配置，以使得同样数量的file能够被切分成更多的object；另一方面，Ceph也推荐PG总数应该为OSD总数的数百倍，以保证有足够数量的PG可供映射。**

### 3. PG -> OSD映射

第三次映射就是将作为object的逻辑组织单元的PG映射到数据的实际存储单元OSD。如图所示，RADOS采用一个名为 **CRUSH** 的算法，将pgid代入其中，然后得到一组共n个OSD。这n个OSD即共同负责存储和维护一个PG中的所有object。前已述及，n的数值可以根据实际应用中对于可靠性的需求而配置，在生产环境下通常为3。具体到每个OSD，则由其上运行的OSD deamon负责执行映射到本地的object在本地文件系统中的存储、访问、元数据维护等操作。

和“object -> PG”映射中采用的哈希算法不同，这个 **CRUSH算法的结果不是绝对不变的** ，而是受到其他因素的影响。

其影响因素主要有二：

一是 当前系统状态，也就是上文逻辑结构中曾经提及的cluster map。当系统中的OSD状态、数量发生变化时，cluster map可能发生变化，而这种变化将会影响到PG与OSD之间的映射。

二是 存储策略配置。这里的策略主要与安全相关。利用策略配置，系统管理员可以指定承载同一个PG的3个OSD分别位于数据中心的不同服务器乃至机架上，从而进一步改善存储的可靠性。

因此，**只有在系统状态（cluster map）和存储策略都不发生变化的时候，PG和OSD之间的映射关系才是固定不变的。** 在实际使用当中，策略一经配置通常不会改变。而系统状态的改变或者是由于设备损坏，或者是因为存储集群规模扩大。好在Ceph本身提供了对于这种变化的自动化支持，因而，即便PG与OSD之间的映射关系发生了变化，也并不会对应用造成困扰。

事实上，Ceph正是需要有目的的利用这种动态映射关系。正是利用了CRUSH的动态特性，**Ceph可以将一个PG根据需要动态迁移到不同的OSD组合上，从而自动化地实现高可靠性、数据分布re-blancing等特性。**

之所以在此次映射中使用CRUSH算法，而不是其他哈希算法，原因之一正是CRUSH具有上述可配置特性，可以根据管理员的配置参数决定OSD的物理位置映射策略；另一方面是因为CRUSH具有特殊的“稳定性”，也即，当系统中加入新的OSD，导致系统规模增大时，大部分PG与OSD之间的映射关系不会发生改变，只有少部分PG的映射关系会发生变化并引发数据迁移。这种可配置性和稳定性都不是普通哈希算法所能提供的。因此，CRUSH算法的设计也是Ceph的核心内容之一，具体介绍可以参考另一篇文章。

至此为止，Ceph通过三次映射，完成了从file到object、PG和OSD整个映射过程。通观整个过程，可以看到，**这里没有任何的全局性查表操作需求。** 至于唯一的全局性数据结构cluster map，在后文中将加以介绍。可以在这里指明的是，cluster map的维护和操作都是轻量级的，不会对系统的可扩展性、性能等因素造成不良影响。



[摘抄](https://tf2jaguar.github.io/ceph-principle.html#RADOS%E7%9A%84%E9%80%BB%E8%BE%91%E7%BB%93%E6%9E%84)

一个可能出现的困惑是：为什么需要同时设计第二次和第三次映射？难道不重复么？

我们可以反过来想像一下，如果没有PG这一层映射，又会怎么样呢？在这种情况下，一定需要采用某种算法，将object直接映射到一组OSD上。**如果这种算法是某种固定映射的哈希算法，则意味着一个object将被固定映射在一组OSD上，当其中一个或多个OSD损坏时，object无法被自动迁移至其他OSD上（因为映射函数不允许），当系统为了扩容新增了OSD时，object也无法被re-balance到新的OSD上（同样因为映射函数不允许）。** 这些限制都违背了Ceph系统高可靠性、高自动化的设计初衷。

如果采用一个动态算法（例如仍然采用CRUSH算法）来完成这一映射，似乎是可以避免静态映射导致的问题。但是，其结果将是各个OSD所处理的本地元数据量爆增，由此带来的计算复杂度和维护工作量也是难以承受的。

例如，在Ceph的现有机制中，一个OSD平时需要和与其共同承载同一个PG的其他OSD交换信息，以确定各自是否工作正常，是否需要进行维护操作。由于一个OSD上大约承载数百个PG，每个PG内通常有3个OSD，因此，一段时间内，一个OSD大约需要进行数百至数千次OSD信息交换。

然而，**如果没有PG的存在，则一个OSD需要和与其共同承载同一个object的其他OSD交换信息。** 由于每个OSD上承载的object很可能高达数百万个，因此，同样长度的一段时间内，一个OSD大约需要进行的OSD间 **信息交换将暴涨至数百万乃至数千万次。** 而这种状态维护成本显然过高。

综上所述，引入PG的好处至少有二：一方面实现了object和OSD之间的动态映射，从而为Ceph的可靠性、自动化等特性的实现留下了空间；另一方面也有效简化了数据的存储组织，大大降低了系统的维护管理开销。理解这一点，对于彻底理解Ceph的对象寻址机制，是十分重要的。



## 数据操作流程

此处将首先以file写入过程为例，对数据操作流程进行说明。

为简化说明，便于理解，此处进行若干假定。首先，假定待写入的file较小，无需切分，仅被映射为一个object。其次，假定系统中一个PG被映射到3个OSD上。

基于上述假定，则file写入流程可以被下图表示：

![K92FgJ.png](https://figure-bed-1304788733.cos.ap-guangzhou.myqcloud.com/typora/202206091417277.png)

如图所示，当某个client需要向Ceph集群写入一个file时，**首先需要在本地完成前边所叙述的寻址流程，** 将file变为一个object，然后 **找出存储该object的一组三个OSD。** 这三个OSD具有各自不同的序号，序号最靠前的那个OSD就是这一组中的Primary OSD，而后两个则依次是Secondary OSD和Tertiary OSD。

找出三个OSD后，**client将直接和Primary OSD通信**，发起写入操作（步骤1）。Primary OSD收到请求后，分别向Secondary OSD和Tertiary OSD发起写入操作（步骤2、3）。**当Secondary OSD和Tertiary OSD各自完成写入操作后，将分别向Primary OSD发送确认信息（步骤4、5）。** 当Primary OSD确信其他两个OSD的写入完成后，则自己也完成数据写入，并向client确认object写入操作完成（步骤6）。

之所以采用这样的写入流程，本质上是为了保证写入过程中的可靠性，尽可能避免造成数据丢失。同时，由于client只需要向Primary OSD发送数据，因此，在Internet使用场景下的外网带宽和整体访问延迟又得到了一定程度的优化。

当然，这种可靠性机制必然导致较长的延迟，特别是，如果等到所有的OSD都将数据写入磁盘后再向client发送确认信号，则整体延迟可能难以忍受。因此，**Ceph可以分两次向client进行确认。** 当各个OSD都将数据写入内存缓冲区后，就先向client发送一次确认，此时client即可以向下执行。待各个OSD都将数据写入磁盘后，会向client发送一个最终确认信号，此时client可以根据需要删除本地数据。

分析上述流程可以看出，在正常情况下，**client可以独立完成OSD寻址操作，而不必依赖于其他系统模块。因此，大量的client可以同时和大量的OSD进行并行操作。同时，如果一个file被切分成多个object，这多个object也可被并行发送至多个OSD。**

从OSD的角度来看，由于同一个OSD在不同的PG中的角色不同，因此，其工作压力也可以被尽可能均匀地分担，从而避免单个OSD变成性能瓶颈。

如果需要读取数据，client只需完成同样的寻址过程，并直接和Primary OSD联系。目前的Ceph设计中，被读取的数据仅由Primary OSD提供。但目前也有分散读取压力以提高性能的讨论。



## 集群维护

前面的介绍中已经提到，由若干个monitor共同负责整个Ceph集群中所有OSD状态的发现与记录，并且共同形成cluster map的master版本，然后扩散至全体OSD以及client。**OSD使用cluster map进行数据的维护，而client使用cluster map进行数据的寻址。**

在集群中，各个monitor的功能总体上是一样的，其相互间的关系可以被简单理解为主从备份关系。因此，在下面的讨论中不对各个monitor加以区分。

略显出乎意料的是，monitor并不主动轮询各个OSD的当前状态。正相反，**OSD需要向monitor上报状态信息。** 常见的上报有两种情况：一是新的OSD被加入集群，二是某个OSD发现自身或者其他OSD发生异常。在收到这些上报信息后，monitor将更新cluster map信息并加以扩散。其细节将在下文中加以介绍。



## Cluster map的实际内容包括

### （1） Epoch，即版本号

Cluster map的epoch是一个单调递增序列。Epoch越大，则cluster map版本越新。因此，持有不同版本cluster map的OSD或client可以简单地通过比较epoch决定应该遵从谁手中的版本。而monitor手中必定有epoch最大、版本最新的cluster map。当任意两方在通信时发现彼此epoch值不同时，将默认先将cluster map同步至高版本一方的状态，再进行后续操作。

### （2）各个OSD的网络地址

### （3）各个OSD的状态

OSD状态的描述分为两个维度：up或者down（表明OSD是否正常工作），in或者out（表明OSD是否在至少一个PG中）。因此，对于任意一个OSD，共有四种可能的状态：

- Up且in：说明该OSD正常运行，且已经承载至少一个PG的数据。这是一个OSD的标准工作状态；
- Up且out：说明该OSD正常运行，但并未承载任何PG，其中也没有数据。一个新的OSD刚刚被加入Ceph集群后，便会处于这一状态。而一个出现故障的OSD被修复后，重新加入Ceph集群时，也是处于这一状态；
- Down且in：说明该OSD发生异常，但仍然承载着至少一个PG，其中仍然存储着数据。这种状态下的OSD刚刚被发现存在异常，可能仍能恢复正常，也可能会彻底无法工作；
- Down且out：说明该OSD已经彻底发生故障，且已经不再承载任何PG。

### （4）CRUSH算法配置参数

CRUSH算法根据种每个设备的权重尽可能概率平均地分配数据。参数有两个，一 表明了Ceph集群的物理层级关系（cluster hierarchy），二 位置映射规则（placement rules）。

## 情景复现

根据cluster map的定义可以看出，其版本变化通常只会由（3）和（4）两项信息的变化触发。而这两者相比，（3）发生变化的概率更高一些。这可以通过下面对OSD工作状态变化过程的介绍加以反映。

### 新OSD上线

一个新的OSD上线后，首先根据配置信息与monitor通信。Monitor将其加入cluster map，并设置为up且out状态，再将最新版本的cluster map发给这个新OSD。

收到monitor发来的cluster map之后，这个新OSD计算出自己所承载的PG（为简化讨论，此处我们假定这个新的OSD开始只承载一个PG），以及和自己承载同一个PG的其他OSD。然后，新OSD将与这些OSD取得联系。**如果这个PG目前处于降级状态** （即承载该PG的OSD个数少于正常值，如正常应该是3个，此时只有2个或1个。这种情况通常是OSD故障所致），**则其他OSD将把这个PG内的所有对象和元数据复制给新OSD。** 数据复制完成后，新OSD被置为up且in状态。而cluster map内容也将据此更新。这事实上是一个自动化的failure recovery过程。当然，**即便没有新的OSD加入，降级的PG也将计算出其他OSD实现failure recovery。**

如果**该PG目前一切正常，则这个新OSD将替换掉现有OSD中的一个**（PG内将重新选出Primary OSD），**并承担其数据。** 在数据复制完成后，新OSD被置为up且in状态，而被替换的OSD将退出该PG（但状态通常仍然为up且in，因为还要承载其他PG）。而cluster map内容也将据此更新。这事实上是一个自动化的数据re-balancing过程。

### OSD异常

如果一个OSD发现和自己共同承载一个PG的另一个OSD无法联通，则会将这一情况 **上报** monitor。此外，如果一个OSD deamon发现自身工作状态异常，也将把异常情况主动 **上报** 报给monitor。

在上述情况下，monitor将把出现问题的OSD的状态设为down且in。如果超过某一预订时间期限，该OSD仍然无法恢复正常，则其状态将被设置为down且out。反之，如果该OSD能够恢复正常，则其状态会恢复为up且in。在上述这些状态变化发生之后，monitor都将更新cluster map并进行扩散。这事实上是自动化的failure detection过程。

### cluster map 小结

由之前介绍可以看出，对于一个Ceph集群而言，即便由数千个甚至更多OSD组成，cluster map的数据结构大小也并不惊人。同时，cluster map的状态更新并不会频繁发生。即便如此，Ceph依然对cluster map信息的扩散机制进行了优化，以便减轻相关计算和通信压力。

- 首先，cluster map信息是以增量形式扩散的。如果任意一次通信的双方发现其epoch不一致，则版本更新的一方将把二者所拥有的cluster map的差异发送给另外一方。
- 其次，cluster map信息是以异步且lazy的形式扩散的。也即，monitor并不会在每一次cluster map版本更新后都将新版本广播至全体OSD，而是在有OSD向自己上报信息时，将更新回复给对方。类似的，各个OSD也是在和其他OSD通信时，将更新发送给版本低于自己的对方。

基于上述机制，Ceph避免了由于cluster map版本更新而引起的广播风暴。这虽然是一种异步且lazy的机制，但根据Sage论文中的结论，**对于一个由n个OSD组成的Ceph集群，任何一次版本更新能够在O(log(n))时间复杂度内扩散到集群中的任何一个OSD上。**

**一个可能被问到的问题是：** 既然这是一种异步和lazy的扩散机制，则在版本扩散过程中，系统必定出现各个OSD看到的cluster map不一致的情况，这是否会导致问题？

答案是：**不会。** 事实上，如果一个client和它要访问的PG内部的各个OSD看到的cluster map状态一致，则访问操作就可以正确进行。而**如果这个client或者PG中的某个OSD和其他几方的cluster map不一致，则根据Ceph的机制设计，这几方将首先同步cluster map至最新状态，并进行必要的数据re-balancing操作，然后即可继续正常访问。**



## 小结

通过上述介绍，我们可以简要了解Ceph究竟是如果基于cluster map机制，并由monitor、OSD和client共同配合完成集群状态的维护与数据访问的。特别的，基于这个机制，事实上可以自然而然的完成自动化的数据备份、数据re-balancing、故障探测和故障恢复，并不需要复杂的特殊设计。这一点确实让人印象深刻。