zoukankan      html  css  js  c++  java
  • [ ceph ] BlueStore 存储引擎介绍

    为什么需要 BlueStore

    首先,Ceph原本的FileStore需要兼容Linux下的各种文件系统,如EXT4、BtrFS、XFS。理论上每种文件系统都实现了POSIX协议,但事实上,每个文件系统都有一点“不那么标准”的地方。Ceph的实现非常注重可靠性,因而需要为每种文件系统引入不同的Walkaround或者Hack;例如Rename不幂等性,等等。这些工作为Ceph的不断开发带来了很大负担。

    其次,FileStore构建与Linux文件系统之上。POSIX提供了非常强大的功能,但大部分并不是Ceph真正需要的;这些功能成了性能的累赘。另一方面,文件系统的某些功能实现对Ceph并不友好,例如对目录遍历顺序的要求,等等。

    另一方面,是Ceph日志的双写问题。为了保证覆写中途断电能够恢复,以及为了实现单OSD内的事物支持,在FileStore的写路径中,Ceph首先把数据和元数据修改写入日志,日志完后后,再把数据写入实际落盘位置。这种日志方法(WAL)是数据库和文件系统标准的保证ACID的方法。但用在Ceph这里,带来了问题:

    数据被写入两遍,即日志双写问题,这意味着Ceph牺牲了一半的磁盘吞吐量。

    1. Ceph的FileStore做了一遍日志,而Linux文件系统自身也有日志机制,实际上日志被多做了一遍。
    2. 对于新型的LSM-Tree类存储,如RocksDB、LevelDB,由于数据本身就按照日志形式组织,实际上没有再另加一个单独的WAL的必要。
    3. 更好地发挥SSD/NVM存储介质的性能。与磁盘不同,基于Flash的存储有更高的并行能力,需要加以利用。CPU处理速度逐渐更不上存储,因而需要更好地利用多核并行。存储中大量使用的队列等,容易引发并发竞争耗时,也需要优化。另一方面,RocksDB对SSD等有良好支持,它为BlueStore所采用。

    另外,社区曾经为了FileStore的问题,提出用LevelDB作存储后端;对象存储转换为KeyValue存储,而不是转换问文件。后来,LevelDB存储没有被推广开,主流还是使用FileStore。但KeyValue的思路被沿用下来,BlueStore就是使用RocksDB来存储元数据的。

    BlueStore 整体架构

    bluestore 的诞生是为了解决 filestore 自身维护一套journal 并同时还需要基于文件系统的写放大问题,并且 filestore 本身没有对 SSD 进行优化,因此 bluestore 相比于 filestore 主要做了两方面的核心工作:

    1. 去掉 journal ,直接管理裸设备
    2. 针对 SSD 进行单独优化

    bluestore 整体架构如下图:

    通过Allocator(分配器)实现对裸设备的管理,直接将数据保存到设备上;同时针对 metadata 使用 RocksDB 进行保存,底层自行封装了一个BlueFS用来对接RocksDB 与裸设备。

    模块划分

    核心模块

    • RocksDB: 存储预写式日志、数据对象元数据、Ceph的omap数据信息、以及分配器的元数据(分配器负责决定真正的数据应在什么地方存储
    • BlueRocksEnv: 与RocksDB交互的接口
    • BlueFS: 小的文件系统,解决元数据、文件空间及磁盘空间的分配和管理,并实现了rocksdb::Env 接口(存储RocksDB日志和sst文件)。因为rocksdb常规来说是运行在文件系统的顶层,下面是BlueFS。 它是数据存储后端层,RocksDB的数据和BlueStore中的真正数据被存储在同一个块物理设备
    • BlockDevice(HDD/SSD): 物理块设备,存储实际的数据

    rocksdb本身是基于文件系统的,不是直接操作裸设备。它将系统相关的处理抽象成Env,用户可用实现相应的接口。BlueRocksEnv是bluestore实现的一个类,继承自rocksdb::EnvWrapper,来为rocksdb提供底层系统的封装。

    为了对接BlueRocksEnv,实现了一个小的文件系统BlueFS,只实现rocksdb Env需要的接口。所有的元数据的修改都记录在BlueFS的日志中,也就是对于BlueFS,元数据的持久化保存在日志中。在系统启动mount这个文件系统时,只需回放日志,就可将所有的元数据都加载到内存中。BluesFS的数据和日志文件都通过块设备保存到裸设备上(BlueFS和BlueStore可以共享裸设备,也可以分别指定不同的设备)。

    bluestore不使用本地文件系统,直接接管裸设备,并且只使用一个原始分区,HDD/SSD所在的物理块设备实现在用户态下使用linux aio直接对裸设备进行I/O操作。由于操作系统支持的aio操作只支持directIO,所以对BlockDevice的写操作直接写入磁盘,并且需要按照page对齐。其内部有一个aio_thread 线程,用来检查aio是否完成。其完成后,通过回调函数aio_callback 通知调用方。

    Allocator 模块

    用来委派具体哪个实际存储块用来存储当前的object数据;同样采用bitmap的方式来实现allocator,同时采用层级索引来存储多种状态,这种方式对内存的消耗相对较小,平均1TB磁盘需要大概35M左右的ram空间。

    BlueStore 模块

    在之前的存储引擎filestore里,对象的表现形式是对应到文件系统里的文件,默认4MB大小的文件,但是在bluestore里,已经没有传统的文件系统,而是自己管理裸盘,因此需要有元数据来管理对象,对应的就是Onode,Onode是常驻内存的数据结构,持久化的时候会以kv的形式存到rocksdb里。

    BlueStore 存储的最常用写路径应该尽量的短,尽量的简单,这样才能有最好的性能,尽快另外的异常处理路径可能是非常复杂的。BlueStore 的设计有如下特色:

    1. Ceph 并不需要POSIX 文件系统。抛弃它,实现一个尽量简单的文件系统,专门给 RocksDB 使用。这个文件系统叫做 BlueFS
    2. 元数据存储在RocksDB中,用KeyValue的方式正合适。而数据不需要文件系统,直接存储在裸块设备上即可。我们在块设备上需要的,其实是一个空间分配器(Allocator)。

    还有一点,BlueStore 中不同组件可以使用不同的设备。例如给 RocksDB 的 WAL 文件配置 NVRAM,给SST文件配备 SSD,给数据文件配备HDD,方案是灵活的。

    BlueStore 的元数据管理

    在涉及写路径之前,先看看Ceph BlueStore 如何管理元数据。首先的问题是,对象如何映射成磁盘数据结构(Ceph 的底层是对象存储,向上封装出块存储、文件存储)?

    Onode代表对象,名字大概是从Linux VFS的Inode沿袭过来的。Onode常驻内存,在RocksDB中以KeyValue形式持久化;

    Onode包含多个lextent,即逻辑extent。Blob通过映射pextent、即物理extent,映射到磁盘上的物理区域。Blob通常包括来自同一个对象的多段数据,但是也可能被其它对象引用。Bnode是对象快照后,被用于多个对象共享数据的。(不太懂)

    BlueStore 的写路径

    写路径包含了对事务的处理,也回答了BlueStore如何解决日志双写问题。

    首先,Ceph的事务只工作于单个OSD内,能够保证多个对象操作被ACID地执行,主要是用于实现自身的高级功能。每个PG(Placement Group,类似Dynamo的vnode,将hash映射到同一个组内的对象组到一起)内有一个OpSequencer,通过它保证PG内的操作按序执行。事务需要处理的写分三种:

    (1)写到新分配的区域。考虑ACID,因为此写不覆盖已有数据,即使中途断电,因为RocksDB中的元数据没有更新,不用担心ACID语义被破坏。后文可见RocksDB的元数据更新是在数据写之后做的。因而,日志是不需要的。在数据写完之后,元数据更新写入RocksDB;RocksDB本身支持事务,元数据更新作为RocksDB的事务提交即可。

    (2)写到Blob中的新位置。同理,日志是不需要的。

    (3)Deferred Writes(延迟写),只用于覆写(Overwrite)情况。从上面也可以看到,只有覆写需要考虑日志问题。如果新写比块大小(min_alloc_size)更小,那么会将其数据与元数据合并写入到RocksDB中,之后异步地把数据搬到实际落盘位置;这就是日志了。如果新写比块大小更大,那么分割它,整块的部分写入新分配块中,即按(1)处理,;不足的部分按(3)中上种情况处理。

    上述基本概述了BlueStore的写处理。可以看到其是如何解决FileStore的日志双写问题的。

    首先,没有Linux文件系统了,也就没有了多余的Journaling of Journal问题。然后,大部分写是写到新位置的,而不是覆写,因此不需要对它们使用日志;写仍然发生了两次,第一次是数据落盘,然后是RocksDB事务提交,但不再需要在日志中包含数据了。最后,小的覆写合并到日志中提交,一次写完即可返回用户,之后异步地把数据搬到实际位置(小数据合并到日志是个常用技巧);大的覆写被分割,整块部分用Append-only方式处理,也绕开了日志的需要。至此,成为一个自然而正常的处理方式。

    BlueFS 的架构

    BlueFS以尽量简单为目的设计,专门用于支持RocksDB;RocksDB总之还是需要一个文件系统来工作的。BlueFS不支持POSIX接口。总的来说,它有这些特点:

    (1)目录结构方面,BlueFS只有扁平的目录结构,没有树形层次关系;用于放置RocksDB的db.wal/,db/,db.slow/文件。这些文件可以被挂载到不同的硬盘上,例如db.wal/放在NVMRAM上;db/包含热SST数据,放在SSD上;db.slow/放在磁盘上。

    (2)数据写入方面,BlueFS不支持覆写,只支持追加(Append-only)。块分配粒度较粗,约1MB。有垃圾回收机制定期处理被浪费掉的空间。

    (3)对元数据的操作记录到日志,每次挂载时重放日志,来获得当前的元数据。元数据生存在内存中,并没有持久化在磁盘上,不需要存储诸如空闲块链表之类的。当日志过大时,会进行重写Compact。

    如果问为什么BlueStore相比FileStore能够提高越一倍的吞吐量,可能在于其更加简单、更加短的写路径;解决了双写问题,大部分数据不再需要在日志中多写一遍;借用RocksDB处理元数据,后者实现成熟,对SSD优化良好。

    总结

    BlueStore 最大的特点是 OSD 可以直接管理裸磁盘设备,并且将对象数据存储在该设备中。另外对象有很多KV属性信息,这些信息之前是存储在文件的扩展属性或者LevelDB当中的。而在BlueStore中,这些信息存储在RocksDB当中。RocksDB本身是需要运行在文件系统之上的,因此为了使用RocksDB存储这些元数据,需要开发一个简单的文件系统(BlueFS)。

    从BlueStore 的设计和实现上看,可以将其理解为用户态下的一个文件系统,同时使用RocksDB来实现BlueStore所有元数据的管理,简化实现。

    对于整块数据的写入,数据直接以aio的方式写入磁盘,再更新RocksDB中数据对象的元数据,避免了filestore的先写日志,后apply到实际磁盘的两次写盘。同时避免了日志元数据的冗余存储占用,因为传统文件系统有他们自己内部的日志和元数据管理机制。

    BlueStore 其实是实现了用户态的一个文件系统。为了实现简单,又使用了RocksDB来实现了BlueStore的所有元数据的管理,简化了实现。

    优点在于:

      对于整块数据的写入,数据直接 AIO 的方式写入磁盘,避免了 filestore的先写日志,后 apply到实际磁盘的两次写盘。
      对于随机IO,直接 WAL 的形式,直接写入 RocksDB 高性能的 KV 存储中。


    参考链接:

    https://blog.csdn.net/u010487568/article/details/79572390
    https://cloud.tencent.com/developer/news/45599

  • 相关阅读:
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Linux下的iwpriv(iwlist、iwconfig)的简单应用
    OCX控件的注册卸载,以及判断是否注册
    .OCX、.dll文件注册命令Regsvr32的使用
  • 原文地址:https://www.cnblogs.com/hukey/p/11910741.html
Copyright © 2011-2022 走看看