BlueStore 是一个 COW 的后端。对于大于最小分配大小的写请求(对于 HDD 是 64KB、SSD 是 16KB),数据会被分配到一个新分配的 extent。当数据持久化之后,对应元数据就会插入到 RocksDB。这允许 BlueStore 提供高效的克隆操作。克隆操作只需要增加所需要的 extent 的引用计数,然后将新写入指向到新的 extent。这允许 BlueStore 对大于最小分配大小的这部分写、部分写请求避免日志双写。
对于小于最小分配大小的写请求,数据和元数据都都会被先保存到 RocksDB 然后将来随事务异步写入到磁盘。这个延迟写的机制有两个目的:
合并提交小 IO 来提高效率——写一个新数据需要两次 IO 而插入到 RocksDB 只需要一次
根据设备类型优化 IO,通过在 HDD 上异步写 64KB 以下的数据来避免在读操作过程中 seek(avoid seeks during reads),再 SSD 让原地覆盖写(in-place overwrite)仅发生在 16KB 以内的 IO。
关于空间分配,BlueStore 使用两个机制来分配空间:FreeList manager 和 Allocator。FreeList 作为一个磁盘当前使用的持久化记录。就像 BlueStore 的所有元数据一样,它首先被保存到 RocksDB。FreeList manager 的第一版实现被设计为通过 offset 和 length 的键值对表示已使用的 region。这个设计的缺陷在于必须对事务进行序列化——为了避免 free list 不一致,需要先删除旧的 key,然后插入新的 key。第二版设计为基于 bitmap。分配和回收操作使用了 RocksDB 的 merge 操作符来反转受影响的 block 所对应的 bit,从而消除了排序这一要求。RocksDB 中的 merge 操作符执行延迟的、原子的读取-修改-写入操作(deferred atomic read-modify-write operation),与原方法相比不会改变语义也不需要查询的开销。
Allocator 负责为新数据分配空间。他保存了一份 free list 的内存拷贝,并且会在分配后通知 FreeList Manager。Allocator 的第一版实现是基于 extent 的,将可用 extent 划分到 2的 n 次幂的容器中(power-of-two-sized bins)。随着磁盘使用量的增加,这个设计容易产生碎片。第二个设计使用索引结构,这个结构在一个“一位表示一个 block”(single-bit-per-block)的描述之上来跟踪块的所有区域(track whole regions of blocks)。通过查询高层和低层索引,可以有效的找到大的和小的 extent。这种实现对每 PB 使用固定的 35MB 内存。
关于 Cache,因为 BlueStore 实现在用户空间且通过 Direct IO 访问磁盘,所以它不能够利用到操作系统的 page cahce。所以 BlueStore 在用户层使用 scan resistant2Q 算法实现了自身的 write-through 缓存。缓存通过 shard 来并发。它使用了和 Ceph OSD 一样的 shard 模式,对到多个集合的请求通过不同 core 来 shard。这样避免了 false sharing,这样同一个 CPU 的上下文始终访问它对应的 2Q 数据结构。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。