# Ceph中的scrub机制 Ceph的一大主要特点是强一致性,这里主要指的是端到端的一致性。传统存储路径上,从 上层应用到内核的文件系统、通用块层、SCSI层到最后的HBA的磁盘控制器,每层都有发生 错误的可能。因此,传统的端到端的解决方案是以数据块校验为主来解决。而在Ceph层面, 更是加入了Ceph自己的客户端、存储逻辑、数据迁移,势必会导致更高的错误概率。 简单说来,Ceph的OSD会定时启动Scrub检查,当符合条件时会进行调度。 ## Scrub的调度 Scrub的调度解决了一个PG在何时启动Scrub机制,主要有以下几种方式: * 手动立即启动执行扫描 * 在后台设置一定的时间间隔,按照间隔的时间来启动。(默认为一天执行一次) * 设置启动的时间段,一般设定一个系统负载比较轻的时间段来执行Scrub操作 ## 与Scrub相关的数据结构 ```cpp mutex sched_scrub_lock; // scrub相关变量的保护锁 int scrubs_pending; // 资源预约成功,正等待scrub的pg的数量 int scrubs_active; // 正在进行scrub的pg的数量 set<ScrubJob> sched_scrub_pg; // pg对应的所有ScrubJob列表 ``` ## Chunky Scrub Chunky Scrub一个清洗对象的一个块,并阻止对该块的写入。对象存储被分割成以哈希边界结尾的块。而对于每个块,都执行下列逻辑: * 阻止对该块的写入 * 从其他副本获取map * 等待推送被应用(在恢复之后) * 等待写刷入到块 * 等待副本的map * 比较/修复所有的scrub map * 等待digest更新应用 ## Deep Scrub Deep Scrub计算每个对象的数据部分的CRC,存储到digest中,并且计算对象header和omap的kv对的CRC,存储到omap_digest。(这里有个疑问,为什么计算omap的CRC需要先获取header对象的CRC呢?) ## 数据结构 ScrubJob ```cpp struct ScrubJob { spg_t pgid; utime_t sched_time; utime_t deadline; ... }; ``` ### 字段解释 * pgid: 需要进行scrub的pg * sched_time: 安排进行scrub的时间。但是在系统负载高或者没有没有在scrub时间段,则scrub会延迟 * deadline: scrub时间的上限,过了该时间必须scrub,不受系统负载和scrub时间段的限制 Scrubber ```cpp struct Scrubber { ... set<pg_shard_t> reserved_peers; bool reserved, reserved_failed; // 是否预约资源,资源预约是否失败 epoch_t epoch_start; // 开始scrub的epoch bool active; // scrub是否开始 bool queue_snap_trim; // int waitting_on; // 等待的副本数 set<pg_shart_t> waiting_on_whom; // 等待的副本 int shallow_errors; // 轻度扫描错误数 int deep_errors; // 深度扫描错误数 int fixed; // 已经修复的对象数 ScrubMap primary_scrubmap; // 主副本的ScrubMap map<pg_shard_t, ScrubMap> received_maps; // 接收到的从副本的ScrubMap ... bool auto_repair; // 是否自动扫描 map<hobject_t, set<pg_shard_t>, hobject_t::BitwiseComparator> missing; // 扫描出的缺失对象 map<hobject_t, set<pg_shard_t>, hobject_t::BitwiseComparator> inconsistent; // 扫描出的不一致的对象 map<hobject_t, list<pair<ScrubMap::object, pg_shard_t> >, hobject_t::BitwiseComparator> authoritative; // 如果所有副本对象中有不一致的对象,authoritative记录了正确对象所在的OSD int num_digest_updates_pending; // 等待更新digest的对象数目 hobject_t start, end; // 需要扫描的对象列表的开始和结尾 eversion_t subset_last_update; // 扫描对象列表中的最新的版本号 bool deep; // 是否为深度扫描 uint32_t seed; // 计算CRC校验的种子 ... }; ``` ## 参考链接 [解析Ceph: 数据的端到端正确性和Scrub机制](https://www.ustack.com/blog/ceph-internal-scrub/) [SCSI中端到端能解决数据完整性问题吗](http://alanwu.blog.51cto.com/3652632/1093600/) [Ceph源码解析 -- Scrub故障检测](http://www.cnblogs.com/chenxianpao/p/5878159.html)