注意

本文档适用于 Ceph 开发版本。

消息码开发者笔记

简介

本文档的每一章都解释了 Ceph 中消息码实现的一个方面。它主要基于示例进行解释,以展示如何工作。

从和向OSD读取和写入编码块

一个消息码存储池将每个对象存储为 K+M 个块。它被分为 K 个数据块和 M 个编码块。存储池配置为大小为 K+M,以便每个块都存储在活动集中的 OSD 中。块的等级作为对象的属性存储。

假设创建了一个消息码存储池,用于使用五个 OSD(K+M = 5),并能承受其中两个的丢失(M = 2)。

When the object NYAN containing ABCDEFGHI写入它时,消息码编码函数将内容分成三个数据块,简单地通过将内容分成三部分:第一个包含ABC,第二个DEF,最后一个GHI。如果内容长度不是 K 的倍数,内容将被填充。该函数还创建两个编码块:第四个包含YXY and the fifth with GQC。每个块都存储在活动集中的 OSD 中。块存储在具有相同名称(NYAN)但位于不同 OSD 上的对象中。必须保留块创建的顺序,并且除了其名称外,还将其作为对象的属性(shard_t)存储。块1包含ABC and is stored on OSD5,而块4包含YXY and is stored on OSD3.

                          +-------------------+
                     name |        NYAN       |
                          +-------------------+
                  content |      ABCDEFGHI    |
                          +--------+----------+
                                   |
                                   |
                                   v
                            +------+------+
            +---------------+ encode(3,2) +-----------+
            |               +--+--+---+---+           |
            |                  |  |   |               |
            |          +-------+  |   +-----+         |
            |          |          |         |         |
         +--v---+   +--v---+   +--v---+  +--v---+  +--v---+
   name  | NYAN |   | NYAN |   | NYAN |  | NYAN |  | NYAN |
         +------+   +------+   +------+  +------+  +------+
  shard  |  1   |   |  2   |   |  3   |  |  4   |  |  5   |
         +------+   +------+   +------+  +------+  +------+
content  | ABC  |   | DEF  |   | GHI  |  | YXY  |  | QGC  |
         +--+---+   +--+---+   +--+---+  +--+---+  +--+---+
            |          |          |         |         |
            |          |          |         |         |
            |          |       +--+---+     |         |
            |          |       | OSD1 |     |         |
            |          |       +------+     |         |
            |          |       +------+     |         |
            |          +------>| OSD2 |     |         |
            |                  +------+     |         |
            |                  +------+     |         |
            |                  | OSD3 |<----+         |
            |                  +------+               |
            |                  +------+               |
            |                  | OSD4 |<--------------+
            |                  +------+
            |                  +------+
            +----------------->| OSD5 |
                               +------+

When the object NYAN从消息码存储池读取时,解码函数读取三个块:块1 containing ABC,块3 containing GHI和块4 containing YXY并重建对象的原始内容ABCDEFGHI。解码函数被告知块25缺失(它们被称为丢失)。块5无法读取,因为OSD4无法服务 (.

解码函数可以在读取三个块后立即调用:OSD2是最慢的,它的块不需要考虑。这种优化在 Firefly 中没有实现。

                          +-------------------+
                     name |        NYAN       |
                          +-------------------+
                  content |      ABCDEFGHI    |
                          +--------+----------+
                                   ^
                                   |
                                   |
                            +------+------+
                            | decode(3,2) |
                            | erasures 2,5|
            +-------------->|             |
            |               +-------------+
            |                     ^   ^
            |                     |   +-----+
            |                     |         |
         +--+---+   +------+   +--+---+  +--+---+
   name  | NYAN |   | NYAN |   | NYAN |  | NYAN |
         +------+   +------+   +------+  +------+
  shard  |  1   |   |  2   |   |  3   |  |  4   |
         +------+   +------+   +------+  +------+
content  | ABC  |   | DEF  |   | GHI  |  | YXY  |
         +--+---+   +--+---+   +--+---+  +--+---+
            ^          .          ^         ^
            |    TOO   .          |         |
            |    SLOW  .       +--+---+     |
            |          ^       | OSD1 |     |
            |          |       +------+     |
            |          |       +------+     |
            |          +-------| OSD2 |     |
            |                  +------+     |
            |                  +------+     |
            |                  | OSD3 |-----+
            |                  +------+
            |                  +------+
            |                  | OSD4 | OUT
            |                  +------+
            |                  +------+
            +------------------| OSD5 |
                               +------+

嵌入码库

使用Reed-Solomon,参数为 K+M,对象 O 通过将其分成块 O1、O2、… OM 并计算编码块 P1、P2、… PK 来编码。任何 K 个块都可以从可用的 K+M 个块中使用,以获得原始对象。如果数据块 O2 或编码块 P2 丢失,可以使用 K+M 个块中的任何 K 个块来修复它们。如果丢失的块超过 M 个,则无法恢复对象。

读取对象 O 的原始内容可以简单地通过 O1、O2、… OM 的连接来实现,因为插件正在使用系统码。否则,必须将块交给消息码库解码方法来检索对象的内容。

性能取决于编码函数的参数,并且也受调用编码函数时使用的包大小的影响(例如 Cauchy 或 Liberation):较小的包意味着更多的调用和更多的开销。

虽然Reed-Solomon被作为默认选项提供,但 Ceph 通过一个抽象API使用,该API设计为允许每个存储池选择实现它的插件,使用存储在去重编码配置文件.

$ ceph osd erasure-code-profile set myprofile \
    crush-failure-domain=osd
$ ceph osd erasure-code-profile get myprofile
directory=/usr/lib/ceph/erasure-code
k=2
m=1
plugin=isa
technique=reed_sol_van
crush-failure-domain=osd
$ ceph osd pool create ecpool erasure myprofile

The 插件中的 key=value 对,并动态加载自目录,并预期实现int __erasure_code_init(char *plugin_name, char *directory)函数,该函数负责在注册表中注册一个从ErasureCodePlugin派生的对象。插件ErasureCodePluginExample读取:

ErasureCodePluginRegistry &instance =
                           ErasureCodePluginRegistry::instance();
instance.add(plugin_name, new ErasureCodePluginExample());

The ErasureCodePlugin派生的对象必须提供一个工厂方法,从中可以生成ErasureCodeInterface对象的具体实现。插件ErasureCodePluginExample读取:

virtual int factory(const map<std::string,std::string> &parameters,
                    ErasureCodeInterfaceRef *erasure_code) {
  *erasure_code = ErasureCodeInterfaceRef(new ErasureCodeExample(parameters));
  return 0;
}

The 参数参数是设置在消息码配置文件中的key=value对的列表,在创建存储池之前。

ceph osd erasure-code-profile set myprofile \
   directory=<dir>         \ # mandatory
   plugin=isa              \ # mandatory
   m=10                    \ # optional and plugin dependent
   k=3                     \ # optional and plugin dependent
   technique=reed_sol_van  \ # optional and plugin dependent

Notes

如果对象很大,在内存中编码和解码它们可能不切实际。然而,当使用RBD一个 1TB 设备被分成许多个 4MB 的独立对象时RGW也会这样做。

编码和解码在 OSD 中实现。尽管它可以在客户端进行读写实现,但 OSD 在进行清理时必须能够自行编码和解码。

由 Ceph 基金会带给您

Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.