存储系统:ceph-14.2.22
操作系统:ubuntu-server-18.04

总体框架


源码分析


ECBackend::objects_read_and_reconstruct

[ 文件路径 ] ceph/src/osd/ECBackend.cc
osd在读数据时,最终通过ECBackend::objects_read_and_reconstruc方法,在该方法中调用ECBackend::start_read_op方法读数据.

void ECBackend::objects_read_and_reconstruct(const map<hobject_t,std::list<boost::tuple<uint64_t, uint64_t, uint32_t>>> &reads,bool fast_read,GenContextURef<map<hobject_t, pair<int, extent_map>> &&> &&func)
{in_progress_client_reads.emplace_back(reads.size(), std::move(func));if (!reads.size()){kick_reads();return;}//构造读object队列map<hobject_t, set<int>> obj_want_to_read;//构造读的循序队列set<int> want_to_read;//获取分片get_want_to_read_shards(&want_to_read);map<hobject_t, read_request_t> for_read_op;//初始化for_read_opfor (auto &&to_read : reads){//构造单个分片对象map<pg_shard_t, vector<pair<int, int>>> shards;//初始化shardsint r = get_min_avail_to_read_shards(to_read.first, want_to_read, false, fast_read, &shards);ceph_assert(r == 0);CallClientContexts *c = new CallClientContexts(to_read.first, this, &(in_progress_client_reads.back()), to_read.second);//初始化for_read_opfor_read_op.insert(make_pair(to_read.first, read_request_t(to_read.second, shards, false, c)));//初始化obj_want_to_readobj_want_to_read.insert(make_pair(to_read.first, want_to_read));}//开始读start_read_op(CEPH_MSG_PRIO_DEFAULT, obj_want_to_read, for_read_op, OpRequestRef(), fast_read, false);return;
}

CallClientContexts::finish

[ 文件路径 ] ceph/src/osd/ECBackend.cc
上面分析到,ECBackend::start_read_op读完数据之后,CallClientContexts类对象会自动调用回调方法finish;finish方法会调用ECUtil::decode对读的数据进行解码.

void finish(pair<RecoveryMessages *, ECBackend::read_result_t &> &in) override
{ECBackend::read_result_t &res = in.second;extent_map result;if (res.r != 0)goto out;ceph_assert(res.returned.size() == to_read.size());ceph_assert(res.errors.empty());for (auto &&read : to_read){pair<uint64_t, uint64_t> adjusted =ec->sinfo.offset_len_to_stripe_bounds(make_pair(read.get<0>(), read.get<1>()));ceph_assert(res.returned.front().get<0>() == adjusted.first &&res.returned.front().get<1>() == adjusted.second);map<int, bufferlist> to_decode;bufferlist bl;for (map<pg_shard_t, bufferlist>::iterator j = res.returned.front().get<2>().begin();j != res.returned.front().get<2>().end(); ++j){to_decode[j->first.shard].claim(j->second);}int r = ECUtil::decode(ec->sinfo, ec->ec_impl, to_decode, &bl);if (r < 0){res.r = r;goto out;}//分割bufferlist trimmed;trimmed.substr_of(bl, read.get<0>() - adjusted.first,std::min(read.get<1>(), bl.length() - (read.get<0>() - adjusted.first)));//将数据插入到resultresult.insert(read.get<0>(), trimmed.length(), std::move(trimmed));res.returned.pop_front();}out://返回读的数据status->complete_object(hoid, res.r, std::move(result));ec->kick_reads();
}

ECUtil::decode

[ 文件路径 ] ceph/src/osd/ECUtil.cc
上面分析到,ECUtil::decode方法是纠删码解码的入口,该方法中主要思路为:依次读取一个条带的数据(数据块+校验块),对该条带数据进行解码,将解码之后的条带数据依次追加到bufferlist中.

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();ceph_assert(total_data_size % sinfo.get_chunk_size() == 0);ceph_assert(out);ceph_assert(out->length() == 0);for (map<int, bufferlist>::iterator i = to_decode.begin(); i != to_decode.end(); ++i){ceph_assert(i->second.length() == total_data_size);}if (total_data_size == 0){return 0;}//for:此处是以某个分片为基点,以当前分片上块为单元,寻找其他分片对应的块//for:集齐k个块数据,拼接成一个条带,以条带为单元进行解码for (uint64_t i = 0; i < total_data_size; i += sinfo.get_chunk_size()){map<int, bufferlist> chunks;//for:依次获取不同分片上的块数据,此处获取一个条带的数据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);//将解码之后的单个条带数据依次追加到out中}return 0;
}

ErasureCode::decode_concat

[ 文件路径 ] ceph/src/erasure-code/ErasureCode.cc
上面分析到,ECUtil::decode方法最终会调用ec_impl->decode_concat对单个条带数据解码,decode_concat是类ErasureCode的方法。该方法会创建一个新的bufferlist对象,该对象用于存放解码之后的条带数据,该条带数据是完整的,没有任何缺失块。然后调用ErasureCode::_decode方法做进一步处理,最后将完整的条带数据追加到主调方法ECUtil::decode的bl参数中.

int ErasureCode::decode_concat(const map<int, bufferlist> &chunks,bufferlist *decoded)
{set<int> want_to_read;//保存块数据的编号//for:初始化want_to_readfor (unsigned int i = 0; i < get_data_chunk_count(); i++){want_to_read.insert(chunk_index(i));}map<int, bufferlist> decoded_map;//存放解码之后完整有序的单个条带数据//对chunks做进一步处理int r = _decode(want_to_read, chunks, &decoded_map);if (r == 0){for (unsigned int i = 0; i < get_data_chunk_count(); i++){//将解码之后的块数据按顺序添加到decoded中,凑成一个条带数据decoded->claim_append(decoded_map[chunk_index(i)]);}}return r;
}

ErasureCode::_decode

[ 文件路径 ] ceph/src/erasure-code/ErasureCode.cc

int ErasureCode::_decode(const set<int> &want_to_read,const map<int, bufferlist> &chunks,map<int, bufferlist> *decoded)
{vector<int> have;have.reserve(chunks.size());//分配空间//for:初始化have数据for (map<int, bufferlist>::const_iterator i = chunks.begin(); i != chunks.end(); ++i){have.push_back(i->first);}//查看数据块是否缺失,如果不缺失,直接将chunks中数据给decoded,然后直接返回if (includes(have.begin(), have.end(), want_to_read.begin(), want_to_read.end())){for (set<int>::iterator i = want_to_read.begin(); i != want_to_read.end(); ++i){(*decoded)[*i] = chunks.find(*i)->second;}return 0;}//如果数据块丢失,则执行解码unsigned int k = get_data_chunk_count();//获取数据块个数unsigned int m = get_chunk_count() - k;//获取校验快个数unsigned blocksize = (*chunks.begin()).second.length();//获取块大小//for:构造出完整的单条带数据for (unsigned int i = 0; i < k + m; i++){//如果数据块丢失,构造出一个空的数据块,凑齐完整的一个条带if (chunks.find(i) == chunks.end()){//构造缺失的块数据,默认数据为空bufferlist tmp;bufferptr ptr(buffer::create_aligned(blocksize, SIMD_ALIGN));tmp.push_back(ptr);tmp.claim_append((*decoded)[i]);(*decoded)[i].swap(tmp);}else{//如果不缺,直接取出数据块中的数据(*decoded)[i] = chunks.find(i)->second;(*decoded)[i].rebuild_aligned(SIMD_ALIGN);}}//对decoded数据进一步处理return decode_chunks(want_to_read, chunks, decoded);
}

[ 注 ] have和want_to_read区别:have中保存的chunks中块的编号,可能有缺失的块。want_to_read是逻辑上块的编号,一定是完整的;

ErasureCodeJerasure::decode_chunks

[ 文件路径 ] ceph/src/erasure-code/jerasure/ErasureCodeJerasure.cc
上面分析到,ErasureCode::_decode最终会调用decode_chunks方法对decoded进一步处理。decoded是类ErasureCodeJerasure的方法。在该方法中会对单条带数据进行分离,分离出数据块和校验块,然后调用接口做进一步处理.

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);
}

ErasureCodeJerasureReedSolomonVandermonde::jerasure_decode

[ 文件路径 ] ceph/src/erasure-code/jerasure/ErasureCodeJerasure.cc
上面分析到,ErasureCodeJerasure::decode_chunks最终会调用jerasure_decode方法做进一步处理,jerasure_decode是ErasureCodeJerasureReedSolomonVandermonde方法。该方法调用纠删码解码C语言接口,纠删码解码方法有很多,目前使用的是范德蒙行列式解码.

int ErasureCodeJerasureReedSolomonVandermonde::jerasure_decode(int *erasures,char **data,char **coding,int blocksize)
{//调用jerasure接口,将data中构造出的空数据用解码出来的数据进行覆盖return jerasure_matrix_decode(k, m, w, matrix, 1, erasures, data, coding, blocksize);
}

附件


附件是我根据Ceph纠删码解码方法,抽离出来的一套C++纠删码解码代码。当出现极端情况,Ceph无法根据纠删码策略完成数据恢复。此时就可以利用这一套代码,执行脚本,即可恢复出原数据。源码获取方式以及使用方法如下
https://download.csdn.net/download/Hao_jiu/85834914

「Ceph源码分析」纠删码解码相关推荐

  1. 全网最新最全的 HDFS 文件纠删码技术分析

    前言 本文隶属于专栏<1000个问题搞定大数据技术体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见1000个问题搞定大数据技 ...

  2. Hadoop 3.0 Erasure Coding 纠删码功能预分析

    前言 HDFS也可以支持Erasure Coding功能了,将会在Hadoop 3.0中发布,可以凭图为证: 在HDFS-7285中,实现了这个新功能.鉴于此功能还远没有到发布的阶段,可能后面此块相关 ...

  3. K8s使用Ceph纠删码池做持久化卷

    K8s使用Ceph纠删码池做持久化卷 Ceph侧准备 Ceph纠删码相关 创建纠删码规则 创建纠删码池 创建复制集池 创建用户并授权 K8s消费ec池 验证 (可选)缓存方式 Kubernetes版本 ...

  4. RS 纠删码为什么可以提高分布式存储可靠性?| 原力计划

    作者 | .NY&XX 来源 | CSDN博客专家,责编 | 夕颜 封图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 前言 Erasure Code(EC),即 ...

  5. 纠删码项目总结(1.0)

    1. 为什么要使用纠删码? 随着数据的持续增长,在 PB 级别的数据中心,多副本技术会引入极大的存 储开销.比如,现有的分布式存储系统,如 HDFS(Hadoop distributed file s ...

  6. HDFS纠删码(Erasure Coding)

    目的 HDFS默认的3副本策略,在存储空间和其他比如网络带宽上有200%的开销,因而副本策略是昂贵的.但是对于具有相对较低I/O的冷热数据集,在正常操作期间很少访问其他副本块,但仍然消耗与第一个副本相 ...

  7. MiniO纠删码快速入门

    MiniO纠删码快速入门 Minio使用纠删码erasure code和校验和checksum来保护数据免受硬件故障和无声数据损坏. 即便您丢失一半数量(N/2)的硬盘,您仍然可以恢复数据. 什么是纠 ...

  8. Erasure code(纠删码)

    1.概述 在编码理论里,有一种前向纠错(FEC)编码方式,也称为纠删码. 这种技术可以将原始数据中丢失的k字节数据从n个含编码字节的信息中进行恢复. 在纠删码技术中,Reed-Solomon(里所码) ...

  9. Ceph 进阶系列(四):Ceph的纠删码特性 EC(Erasure Code)

    从GitHub上Clone Ceph项目,我是基于(ceph version 12.2.11 luminous 版本)的代码来分析的 一.EC(Erasure Code)是什么? Ceph的纠删码特性 ...

最新文章

  1. cocos2d-x 3.0游戏实例学习笔记 《跑酷》第四步--地图循环amp;主角加入动作
  2. 计算机视觉行业迎来大发展,多角度解读未来前景
  3. Twin Delayed DDPG(TD3)-强化学习算法
  4. 解决开源矿工笔记本屏幕不能关闭的问题
  5. 距离高考出成绩,一年了、、、
  6. java内容置剪贴板
  7. linux-2.6.32在mini2440开发板上移植(16)之LED 驱动程序移植
  8. 构建虚拟工控环境系列 - 罗克韦尔虚拟PLC
  9. viper4android 魅族6,手机资讯导报:魅族自曝新旗舰PRO6摄像头不凸起
  10. SQL Server高级查询之子查询(多行子查询)
  11. Linux内核源码阅读之系统调用mmap()
  12. excel 第20讲:图表基础
  13. jntoo.php,小舍微信分销拼团系统V6.0三级分销系统最新版源码+支持多商户入驻+拼团+微商城+分销...
  14. 道路施工安全智能预警
  15. 微信营销为什么需要云控
  16. android补间动画有哪几种,android三种动画详解(补间动画、帧动画、属性动画)...
  17. 五一结婚,收集祝福。附我的结婚对联,结婚放大像。
  18. win10睡眠只是关掉显示器的问题
  19. 区块链学习笔记23——ETH反思
  20. 毕业论文关键字HTML5,毕业论文关键词的选择

热门文章

  1. 管理是个难题,向你讲述小企业经验
  2. 200行JS代码为你的网页挂上红灯笼
  3. 关于采集插件的一些详细知识
  4. WordPress彻底禁用上传媒体图片自动生成缩略图及多尺寸图片(亲测可用)
  5. Java面试锦囊(五)
  6. mysql 分类标签表设计_如何进行文章分类和标签的数据库设计
  7. antv/G2 v4使用遇坑之旅
  8. 【数据库视频】报表服务
  9. ko 绑定html,WeX5的正确打开方式(3)——绑定机制
  10. BYOD携带自己的设备办公