BlueStore checksum机制
1.checksum的计算
2.checksum的验证算法
BlueStore 磁盘静默数据损坏分析数据库
实际场景
1.腾讯场景问题确认
2.rbd mirror
3.rbd export import
4.集群扩容数组
Bluestore的checksum算法默认采用的是crc32, 算法的逻辑很简单,即以固定大小的数据块为单位进行计算checksum,数据块的大小通常为4K,同设备的块大小。
Checksum的结果做为bluestore_blob_t结构体序列化的一部分存放在数据库中,假设一个blob的长度为64KB,进行checksum的数据块大小为4KB, 则初始化该blob时,对于crc32,每一个数据块的checksum用4字节描述,将分配(64KB/4KB)* 4 = 64字节用于存储该blob数据的checksum。 ide
写数据会进行checksum的计算,对于对齐写和非对齐写,都会以数据所在的4KB对齐的数据块为单位进行计算。好比0-256区间的数据更新,会将257-4095区间的数据读出来,而后以4KB为单位进行计算,若是257-4095原来不存在数据,则用0进行填充后进行计算,计算完将checksum值存储在该blob中。 测试
读数据时,bluestore中会首先查找所读取数据区间所对应的checksum数组,而后用读取的数据计算checksum,判断二者是否一致。举例以下:假设blob的大小为64KB,
本次读取的数据区间为8192~16383, 则分别计算8192~12287和12288-16383两个数据块的checksum,而后读取blob中以前写数据时计算的checksum,这两块数据对应的checksum值为checksum数组偏移为2和3的两个4字节变量。日志
BlueStore中以下五种操做会读取对象数据,其中目前只有对象数据read操做能主动触发对象修复操做,且对上层用户不感知。code
结论:读数据的时候依靠checksum机制能够发现数据不一致的状况,其中read能够自动修复,其余的读取操做若是发现存在静默错误,会及时报错,因此能够取消按期deep-scrub操做。对象
==>> int BlueStore::_mount(bool kv_only, bool open_db)
====>> fsck(cct->_conf->bluestore_fsck_on_mount_deep)
若是bluestore_fsck_on_mount_deep为真,bluestore的fsck过程当中将会读取整个文件,以此将会检查该文件对应的磁盘块是否存在数据损坏现象,若是文件损坏,会致使mount失败,从而致使该osd启动失败。进程
//bluestore_fsck_on_mount_deep 默认为true Option("bluestore_fsck_on_mount_deep", Option::TYPE_BOOL, Option::LEVEL_DEV) .set_default(true) .set_description("Run deep fsck at mount"),
Clone操做是否从磁盘读数据取决于bluestore_clone_cow配置,若是bluestore_clone_cow为真,则直接进行extent的共享,不会有实际的数据读写操做。若是为假,读取被clone对象的数据,写入新的对象,若是此时读取过程当中,发生磁盘静默错误,则直接assert,osd进程down掉。事务
//bluestore_clone_cow 默认为true Option("bluestore_clone_cow", Option::TYPE_BOOL, Option::LEVEL_ADVANCED) .set_default(true) .set_safe() .set_description("Use copy-on-write when cloning objects (versus reading and rewriting them at clone time)"),
读操做过程当中,若是发现磁盘静默数据错误,是会主动对该对象进行修复的,代码调用流程以下:
==>> do_osd_ops ====>> do_read ======>> objects_read_sync ======>> rep_repair_primary_object
将对象加入待修复列表,进行修复,读操做 会从新发起,用户不感知。
Write操做的rmw写,须要首先从磁盘读取数据进行覆盖写,若是此时发生磁盘静默错误,代码中直接assert,代码调用流程以下:
==>> _do_write_small
====>> _do_read 触发( assert(r >= 0 && r <= (int)tail_read);)
一般ext4类的本地文件系统会直接abort事务,将文件系统挂载为只读,所以此处直接assert osd,致使osd进程down也能够理解。
==>> _do_write
====>> _do_gc
======>> _do_read 触发assert(r == (int)it->length);
此处也会致使osd down,策略我理解同write操做。
1.1 cache?1.2 no crc? 1.3 migration?
ceph社区邮件”Copying without crc check when peering may lack reliability”提到的磁盘静默数据损坏没有及时发现的问题,根据发件人的测试步骤没有复现,数据都能自动修复,目前正在请求发件人提供完整复现脚本。现阶段调查结果以下:
1, ceph-objectstore-tool修改对象数据时,若是checksum使能,会同步用新的数据计算checksum,若是修改对象主副本,则新数据迁移到新的crush root下,此处是合理的,由于checksum检查经过,osd会认为数据是正确的,若是修改对象的从副本,因为迁移是根据主副本的数据,新的crush root下的数据仍然是主副本数据。
若是对该osd作deep-scrub, 若是对象所属的pg在该osd上是主pg,是能检测到数据变更的,由于会和从pg上的副本作对比,若是对象所属的pg在该osd上为从pg, deep-scrub将不会针对该pg作检查的。
2,若是对象数据的主副本存在cache,即便对应的底层硬件存在静默错误,正常的读操做和deep-scrub都不会检测到磁盘静默错误(我已经发送patch让deep-scrub能检测出此种损坏,patch已经被merge)。
3,数据迁移在ceph master分支上验证,在三副本测试中,破坏一个副本的数据,而后让数据迁移到另外一个crush root下,ceph会自动进行数据修复的,用户不感知。
测试步骤:
破坏journal对象的主副本数据,而后进行replay,待replay完成后检查两个rbd设备的数据是否一致。
测试结果:
暂时没有找到测试方法来控制上述流程,但理论分析是能够修复的,用户不感知道。Rbd-mirror回放日志数据,也就是读取日志对象主副本数据,具体流程同”3. read操做”。
测试步骤:
建立一个rbd设备,向其偏移0处写入4MB随机数据,而后经过dd破坏其中一段数据,注意破坏是主副本的数据。破坏完成后,将主副本所在的osd重启以消除cache的影响,而后进行rbd的export 和import测试,读取两个rbd设备的前4MB数据MD5值与初始的写入rbd设备的随机数据文件md5值是否一致。
测试结果:
损坏的rbd对象数据会自动进行修复,用户不感知,具体原理参见”3. read操做”。
测试步骤:
建立3 osd集群,用rados建立一个对象,损坏对象主副本数据,注意一样须要重启osd以清除bluestore层cache,加入3个新的osd,待集群稳定后,用rados读取对象数据,验证数据md5值是否一致。
测试结果: 集群rebalance成功,损坏的数据会自动进行修复,用户不感知,具体原理参见”3. read操做”。