ceph源码阅读 erasure-code

1、ceph纠删码

纠删码(Erasure Code)是比较流行的数据冗余的存储方法,将原始数据分成k个数据块(data chunk),通过k个数据块计算出m个校验块(coding chunk)。把n=k+m个数据块保存在不同的节点,通过n中的任意k个块还原出原始数据。EC包含编码和解码两个过程。

ceph中的EC编码是以插件的形式来提供的。EC编码有三个指标:空间利用率、数据可靠性和恢复效率。ceph提供以下几种纠删码插件:clay(coupled-layer)、jerasure、lrc、shec、isa。

clay:用于在修复失败的节点/OSD/rack时节省网络带宽和磁盘IO。

jerasure:开源库,目前ceph默认的编码方式。

isa:isa是Intel提供的EC库,利用intel处理器指令加速计算,只能运行在Intel CPU上。

lrc:将校验块分为全局校验块和局部校验块,减少单个节点失效后恢复过程的网络开销。

shec:shec(k,m,l),k为data chunk,m为coding chunk,l代表计算coding chunk时需要的data chunk数量。其最大允许失效数据块为:ml/k,恢复失效的单个数据块(data chunk)只需要额外读取l个数据块。

2、erasure-code文件结构

erasure-code是ceph的纠删码核心模块,包含5个文件夹和ErasureCodeInterface、ErasureCode、ErasureCodePlugin,采用工厂模式。

  • clay、isa、jerasure、lrc、shec是ceph的EC插件。
  • ErasureCodeInterface:提供EC插件的共有接口,每个接口有详细的说明。
  • ErasureCode:提供获取纠删码参数和核心编码、解码接口。
  • ErasureCodePlugin:提供纠删方式事件的注册、添加、删除登功能。

3、数据条带化

存储设备都有吞吐量限制,它会影响性能和伸缩性,所以存储系统一般都支持条带化(把连续的信息分片存储于多个设备)以增加吞吐量和性能。

  • 基本概念

块(chunk):基于纠删码编码时,每次编码将产生若干大小相同的块(要求这些块时有序的,否则无法解码)。ceph通过数量相等的PG将这些分别存储在不同的osd中。

条带(strip):如果编码对象太大,可分多次进行编码,每次完成编码的部分称为条带。同一个对内的条带时有序的。

分片(shared):同一个对象中所有序号相同的块位于同一个PG上,他们组成对象的一个分片,分片的编号就是块的序号。

空间利用率(rate):通过k/n计算。

对象尺寸: Ceph 存储集群里的对象有最大可配置尺寸(如 2MB 、 4MB 等等),对象尺寸必须足够大以便容纳很多条带单元、而且应该是条带单元的整数倍。

条带数量: Ceph 客户端把一系列条带单元写入由条带数量所确定的一系列对象,这一系列的对象称为一个对象集。客户端写到对象集内的最后一个对象时,再返回到第一个。

条带宽度: 条带都有可配置的单元尺寸(如 64KB )。 Ceph 客户端把数据等分成适合写入对象的条带单元,除了最后一个。条带宽度应该是对象尺寸的分片,这样对象才能 包含很多条带单元。

strip_width=chunk_size*strip_size

假设有EC(k=4,m=2),strip_size=4,chunk_size=1K,那么strip_width=4K。在ceph中,strip_width默认为4K。

  • 数据条带化过程

如果要处理大尺寸图像、大 S3 或 Swift 对象(如视频)、或大的 CephFS 目录,你就能看到条带化到一个对象集中的多个对象能带来显著的读/写性能提升。当客户端把条带单元并行地写入相应对象时,就会有明显的写性能,因为对象映射到了不同的归置组、并进一步映射到不同 OSD ,可以并行地以最大速度写入。

在上图中,客户端数据条带化到一个对象集(上图中的objectset1),它包含 4 个对象,其中,第一个条带单元是object0的stripe unit 0、第四个条带是object3的stripe unit 3,写完第四个条带,客户端要确认对象集是否满了。如果对象集没满,客户端再从第一个对象起写入条带(上图中的object0);如果对象集满了,客户端就得创建新对象集(上图的object set 2),然后从新对象集中的第一个对象(上图中的object 4)起开始写入第一个条带(stripe unit16)。

4、源码解析

  • 编码流程

ECUtil::encode是将原始数据按条带宽度进行分割,然后对条带数据编码,得到条带的数据块和校验块。把每个条带化数据块和校验块有序的链接,形成数据块和校验块。

int ECUtil::encode(const stripe_info_t &sinfo,ErasureCodeInterfaceRef &ec_impl,bufferlist &in,const set<int> &want,map<int, bufferlist> *out)
....//文件每次按strip_width的大小进行encode编码for (uint64_t i = 0; i < logical_size; i += sinfo.get_stripe_width()) {map<int, bufferlist> encoded;bufferlist buf;buf.substr_of(in, i, sinfo.get_stripe_width());//调用对应的纠删码方式进行编码int r = ec_impl->encode(want, buf, &encoded);ceph_assert(r == 0);//将条带化的数据块和校验块追加到outfor (map<int, bufferlist>::iterator i = encoded.begin();i != encoded.end();++i) {ceph_assert(i->second.length() == sinfo.get_chunk_size());(*out)[i->first].claim_append(i->second);}}....return 0;
}

接下来深入分析ec_impl->encode(want, buf,&encoded),ErasureCode是ErasureCodeInterface的子类,因此调用ErasureCode::encode。在ErasureCode::encode主要是进行map<int, bufferlist>*encoded的内存分配(encode_prepare())和数据块的编码(encode_chunks)。

int ErasureCode::encode(const set<int> &want_to_encode,const bufferlist &in,map<int, bufferlist> *encoded)
{unsigned int k = get_data_chunk_count();unsigned int m = get_chunk_count() - k;bufferlist out;//encoded的内存块分配int err = encode_prepare(in, *encoded);if (err)return err;//进行编码操作encode_chunks(want_to_encode, encoded);for (unsigned int i = 0; i < k + m; i++) {if (want_to_encode.count(i) == 0)encoded->erase(i);}return 0;
}

ErasureCode::encode_prepare()进行参数初始化和内存分配。

int ErasureCode::encode_prepare(const bufferlist &raw,map<int, bufferlist> &encoded) const
{unsigned int k = get_data_chunk_count();unsigned int m = get_chunk_count() - k;//每个块的大小unsigned blocksize = get_chunk_size(raw.length());//空白块的个数unsigned padded_chunks = k - raw.length() / blocksize;bufferlist prepared = raw;//将数据raw按blocksize大小有序分割,并将分割后的块有序写入到encodedfor (unsigned int i = 0; i < k - padded_chunks; i++) {bufferlist &chunk = encoded[chunk_index(i)];chunk.substr_of(prepared, i * blocksize, blocksize);chunk.rebuild_aligned_size_and_memory(blocksize, SIMD_ALIGN);ceph_assert(chunk.is_contiguous());}if (padded_chunks) {unsigned remainder = raw.length() - (k - padded_chunks) * blocksize;bufferptr buf(buffer::create_aligned(blocksize, SIMD_ALIGN));raw.copy((k - padded_chunks) * blocksize, remainder, buf.c_str());buf.zero(remainder, blocksize - remainder);encoded[chunk_index(k-padded_chunks)].push_back(std::move(buf));for (unsigned int i = k - padded_chunks + 1; i < k; i++) {bufferptr buf(buffer::create_aligned(blocksize, SIMD_ALIGN));buf.zero();encoded[chunk_index(i)].push_back(std::move(buf));}}for (unsigned int i = k; i < k + m; i++) {bufferlist &chunk = encoded[chunk_index(i)];chunk.push_back(buffer::create_aligned(blocksize, SIMD_ALIGN));}return 0;
}

以上的工作完成后,可以开始正式的编码encode_chunks()。这里假设编码方式为jerasure,选用RS码。

int ErasureCodeJerasure::encode_chunks(const set<int> &want_to_encode,map<int, bufferlist> *encoded)
{char *chunks[k + m];for (int i = 0; i < k + m; i++)chunks[i] = (*encoded)[i].c_str();jerasure_encode(&chunks[0], &chunks[k], (*encoded)[0].length());return 0;
}

jerasure_encode()是调用jerasure库的编码函数。

void ErasureCodeJerasureReedSolomonVandermonde::jerasure_encode(char **data,char **coding,int blocksize)
{jerasure_matrix_encode(k, m, w, matrix, data, coding, blocksize);
}
  • 解码流程

ECUtil::decode函数有两个,挑个简单的来分析。

下面的decode()函数初始化数据,并进行解码。

int ECUtil::decode(const stripe_info_t &sinfo,ErasureCodeInterfaceRef &ec_impl,map<int, bufferlist> &to_decode,bufferlist *out) {ceph_assert(to_decode.size());uint64_t total_data_size = to_decode.begin()->second.length();....for (uint64_t i = 0; i < total_data_size; i += sinfo.get_chunk_size()) {map<int, bufferlist> chunks;for (map<int, bufferlist>::iterator j = to_decode.begin();j != to_decode.end();++j) {chunks[j->first].substr_of(j->second, i, sinfo.get_chunk_size());}bufferlist bl;int r = ec_impl->decode_concat(chunks, &bl);ceph_assert(r == 0);ceph_assert(bl.length() == sinfo.get_stripe_width());out->claim_append(bl);}return 0;
}

ErasureCode::decode_concat()进行解码,并且链接哥哥数据块,还原出原数据。

int ErasureCode::decode_concat(const map<int, bufferlist> &chunks,bufferlist *decoded)
{set<int> want_to_read;for (unsigned int i = 0; i < get_data_chunk_count(); i++) {want_to_read.insert(chunk_index(i));}map<int, bufferlist> decoded_map;//解码核心部分int r = _decode(want_to_read, chunks, &decoded_map);if (r == 0) {//将解码后的数据块链接for (unsigned int i = 0; i < get_data_chunk_count(); i++) {decoded->claim_append(decoded_map[chunk_index(i)]);}}return r;
}

同样地,这里也是假设使用纠删码插件为jerasure。jerasure_decode()是调用jerasure库的解码函数。

int ErasureCodeJerasure::decode_chunks(const set<int> &want_to_read,const map<int, bufferlist> &chunks,map<int, bufferlist> *decoded)
{unsigned blocksize = (*chunks.begin()).second.length();int erasures[k + m + 1];//记录丢失块的编号int erasures_count = 0;//丢失块的个数char *data[k];char *coding[m];for (int i =  0; i < k + m; i++) {if (chunks.find(i) == chunks.end()) {erasures[erasures_count] = i;erasures_count++;}if (i < k)data[i] = (*decoded)[i].c_str();elsecoding[i - k] = (*decoded)[i].c_str();}erasures[erasures_count] = -1;ceph_assert(erasures_count > 0);return jerasure_decode(erasures, data, coding, blocksize);
}

参考资料:

1、体系结构 - Ceph Documentation

2、ceph源码分析 常涛

3、ceph设计原理与实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/114329.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过HTTP进行并发的数据抓取

在进行大规模数据抓取时&#xff0c;如何提高效率和稳定性是关键问题。本文将介绍一种可操作的方案——使用HTTP代理来实现并发的网页抓取&#xff0c;并帮助您加速数据抓取过程。 1. 选择合适的HTTP代理服务供应商 - 寻找信誉良好、稳定可靠且具备较快响应时间的HTTP代理服务…

go学习part21 Redis和Go(2)

1.三方库安装 309_尚硅谷_Go连接到Redis_哔哩哔哩_bilibili 借鉴&#xff1a; Golang 安装 Redis_go fiber 安装redis_柒柒伍贰玖。的博客-CSDN博客 三方redis库已经迁移到以下网址&#xff0c;go get github.com/gomodule/redigo/redis gomodule/redigo: Go client for Red…

Python UDP编程

前面我们讲了 TCP 编程&#xff0c;我们知道 TCP 可以建立可靠连接&#xff0c;并且通信双方都可以以流的形式发送数据。本文我们再来介绍另一个常用的协议--UDP。相对TCP&#xff0c;UDP则是面向无连接的协议。 UDP 协议 我们来看 UDP 的定义&#xff1a; UDP 协议&#xff…

springboot整合jquery实现前后端数据交互

一 实施逻辑 1.1 前端 <!doctype html> <html lang"en"><head><meta charset"UTF-8"><meta name"Generator" content"EditPlus"><meta name"Author" content""><meta n…

FFDNet-pytorch版本代码训练教程

一、FFDNet-pytorch版本代码下载 (1)FFDNet-pytorch下载 https://download.csdn.net/download/qq_41104871/88233742 (2)FFDNet-pytorch版本代码运行环境配置 https://blog.csdn.net/qq_41104871/article/details/132497008 二、FFDNet-pytorch版本代码训练教程 (1)按…

并发编程的故事——共享模式之无锁

共享模式之无锁 文章目录 共享模式之无锁一、提出问题二、CAS和volatile三、原子整数四、原子引用五、原子数组六、原子更新器七、原子累加器八、unsafe 一、提出问题 关于对共享变量修改的多线程问题其实就是指令交错问题导致取值的时机相同&#xff0c;最后修改之后以最后一…

Rabbitmq安装

1、安装说明 安装RabbitMq时需注意&#xff0c;需要先安装Erlang。因为RabbitMq依赖于Erlang&#xff0c;且两者之间的版本是有对应关系的&#xff0c;详细可查看&#xff1a;版本对照表 此外&#xff0c;需要注意的是本教程中采用的安装方式是使用源码安装。非rpm或一键安装方…

精读《算法题 - 地下城游戏》

今天我们看一道 leetcode hard 难度题目&#xff1a;地下城游戏。 恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士…

【若依框架RuoYi-Vue-Plus 图片回显不显示问题,OSS文件上传或者本地上传】

一、问题 1.设计表 product&#xff08;商品表&#xff09; 有 id &#xff08;id&#xff09; name&#xff08;商品名&#xff09;icon&#xff08;图标&#xff09; 2.使用若依代码生成功能&#xff0c;导入product表&#xff0c;代码生成。 3.将生成的代码导入到项目中得到…

线性代数的学习和整理16:什么是各种空间(类型),向量空间,距离(类型)?

目录 1 空间相关的群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的预备知识 1.1&#xff1a;群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的定义&#xff08;表示不懂&#xff0c;只是做个标记&#xff09; 2 空间 2.1 各种空间概念…

客路旅行(KLOOK)面试(部分)(未完全解析)

一面 用过Chatgpt的哪个版本&#xff0c;了解Chatgpt版本之间的差异吗 什么是优雅部署&#xff1f;newBing: 服务启动时&#xff0c;检查依赖的组件或容器是否就绪&#xff0c;如果不就绪&#xff0c;等待或重试&#xff0c;直到就绪后再注册到服务中心&#xff0c;对外提供服…

Spring三级缓存解决循环依赖

Spring三级缓存解决循环依赖 一 Spring bean对象的生命周期 二 三级缓存解决循环依赖 实现原理解析 spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的&#xff0c;所说的缓存其实也就是三个Map 先实例化的bean会通过ObjectFactory半…

Ubuntu学习---跟着绍发学linux课程记录(第一部分)

文章目录 1、启动、关闭、挂起、恢复&#xff08;电源&#xff09;2、更多虚拟机操作2.1 电源设置2.2 硬件参数设置2.3 状态栏2.4 全屏显示 3、快照与系统恢复4、桌面环境5、文件系统6、用户目录7、创建目录和文件8、命令行&#xff1a;文件列表ls 9、命令行&#xff1a;切换目…

skywalking agent监控java服务

一、前言 skywalking agent可以监控的服务类型有多种&#xff0c;python、go、java、nodejs服务等都可以监控&#xff0c;现在通过java服务来演示skywalking agent的使用&#xff0c;并且是使用容器的方式实现 二、部署skywalking agent监控 需要注意&#xff0c;skywalking…

JVM类加载器

一、类与类加载器 类加载器虽然只用于实现类的加载动作&#xff0c;但它在Java程序中起到的作用却远超类加载阶段。对于 任意一个类&#xff0c;都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性&#xff0c;每一个类加载器&#xff0c;都拥有一个独…

每日一题:leetcode 1448 统计二叉树中好节点的数目

给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&#xff1a;从根到该节点 X 所经过的节点中&#xff0c;没有任何节点的值大于 X 的值。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,3,null,1,5] 输出&#xff1a;4 解释&a…

群晖NAS:DSM7.1激活Advanced Media Extensions【自留记录】

群晖NAS&#xff1a;DSM7.1激活Advanced Media Extensions【自留记录】 本文仅半白群晖可用&#xff0c;不需要安装其他套件或者ssh修改什么 使用DS Video 网页播放视频时候&#xff0c;提示&#xff1a;【不支持当前所选音轨的文件格式&#xff0c; 因此无法播放视频。请尝试…

Vue-Router 一篇搞定 Vue3

前言 在 Web 前端开发中&#xff0c;路由是非常重要的一环&#xff0c;但是路由到底是什么呢&#xff1f; 从路由的用途上讲 路由是指随着浏览器地址栏的变化&#xff0c;展示给用户不同的页面。 从路由的实现原理上讲 路由是URL到函数的映射。它将 URL 和应用程序的不同部分…

Leetcode415 字符串相加

思路&#xff1a; 从末尾开始相加&#xff0c;进位可以最后统一处理&#xff0c;因为再怎么进也是最多只进一位 class Solution:def addStrings(self, num1: str, num2: str) -> str:# 确保1里是更长的字符串if len(num1) < len(num2):num1_list list(num2)num2_list …

解决Spring Data JPA中的NullPointerException问题

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…