[ Ceph ] 分布式存储系统 Ceph 后端存储引擎说明

前言

在知乎看到一篇介绍 Ceph 后端存储引擎的文章 【分布式存储系统Ceph的后端系统引擎研究】分析的非常到位,对理解Ceph 后端存储引擎有非常大的帮助。
本篇主要围绕这篇文章来学习,并写出自己想理解和补充说明。

Ceph是一个可靠的、自治的、可扩展的分布式存储系统,它支持文件系统存储、块存储、对象存储三种不同类型的存储,满足多样存储的需求。Ceph沿袭了传统在本地文件系统上面构建存储后端的方法,并受益于十年来经过测试的代码的便利性和成熟性,但是,在带来便利的同时,也相应地限制了Ceph的发展。比如,原始Ceph后端存储引擎FileStore需要先将磁盘操作转换为服务端主机操操作系统识别的文件系统,其实这个转换的过程相当繁杂。除此以外,零开销事务机制的挑战,元数据的管理,以及新型存储硬件的生产,也推动着Ceph后端存储的不断发展,于是2015年Ceph开发团队提出了BlueStore新型后端存储。BlueStore直接将对象数据保存在原始块设备中,绕过本地文件系统层,避免了文件系统开销和其性能缺陷,并采用RocksDB管理其元数据,提高了存储效率。BlueStore表现出来的优越性,使得其在生产中被70%的用户所采用。此次报告将主要针对Ceph现有的存储引擎进行多方位的对比并进行测试,进一步理解分布式后端存储的实现原理。

个人理解:
    1. Ceph 后端存储引擎 FileStore 是建立在文件系统之上的存储引擎;
    2. Ceph存储引擎包括:事务机制、元数据管理、存储硬件(通常指硬盘);
    3. BlueStore特性
        a. 直接将对象数据保存在原始块设备上,不需要在将磁盘转换为操作系统认识的文件系统,避免了文件系统来带开销和性能的缺陷;
    b. 采用RocksDB 管理元数据,提高存储效率。

 

引言

随着近年来多媒体技术和互联网技术的爆发式发展,使得信息技术领域中的数据规模越来越大,传统的企业在信息化的浪潮下也不断地开始将数据信息化,云计算的概念应运而生。云计算的三架马车:计算、网络和存储也逐渐成为工业界和学术界重点研究的领域。作为三驾马车之一的存储,传统的存储系统无法满足呈爆炸性增长的海量数据存储需求,为解决信息存储容量、数据备份、数据安全等问题,分布式存储系统应运而生,在近年来的研发过程中也产生了很多优秀的产品,应用到了实际的生产环境中。Ceph则是众多优秀开源产品中的一个,作为现如今在云计算领域应用最为广泛的分布式存储系统,其高扩展性、高性能和高可靠性等特点使得Ceph成为了云计算厂商的最佳选择。Ceph是由学术界于2006年提出的分布式存储系统的解决方案,一开始致力于解决分布式文件系统中存在的问题,发展到现在已经不再仅仅支持文件系统的存储需求,还提供了块设备和对象存储的接口,使得Ceph能够应用到更多的生产环境和实际应用场景。

本文阐述了分布式存储系统Ceph的架构和实现原理,主要针对Ceph底层的三种存储引擎进行介绍。结合Ceph后端存储引擎的发展历程,来深入比较文件系统、键值存储系统和裸设备作为存储后端的优劣,并延伸到文件系统、键值存储系统和块设备裸盘IO各自独立的应用和局限,旨在帮助学习研究人员进一步了解Ceph的存储后端和现如今应用广泛的各类存储引擎。

 

个人理解:
    1. 云计算三驾马车:计算、网络和存储;
    2. Ceph 特点:高扩展性、高性能和高可靠性;
    3. Ceph最初仅支持文件系统存储方式,后来发展到支持块存储和对象存储接口;
  4. 本文介绍文件系统、键值存储系统和裸设备作为后端存储的优劣。

 

Ceph概述

Ceph架构

Ceph是一个可靠的、自治的、可扩展的分布式存储系统,它支持文件系统存储、块存储、对象存储三种不同类型的存储,满足多样存储的需求。Ceph整体架构包含最上层的存储接口层,用来提供客户端访问存储后端服务的各种接口,Ceph支持块存储、文件存储和对象存储,其中间层是librados,它用来提供各种访问底层RADOS集群存储系统的各种库函数。RADOS层是Ceph的重要组成部分,它提供给用户一个完整的存储系统,其主要组成部分是Monitors、OSDs、MDS,其中Monitor主要功能是用来监视Ceph集群的健康状态,并保存各种map(节点健康状况、逻辑和物理关系映射等)信息,OSDs是运行在存储设备上的主要用于存储数据的守护进程,它以PG为单位进行数据的存储、迁移、和恢复,MDS是文件系统的元数据管理服务进程,主要针对CephFS中的元数据信息进行管理。在librados的基础之上又根据具体的上层应用需求抽象出了块设备的库librbd、对象存储的库librgw和文件系统库libcephfs。Ceph架构分别如下图2.1和图2.2所示。

 

 

 

 

 个人理解:
    1. Ceph支持文件系统存储、块存储、对象存储;
    2. 中间层 librados 是提供上层接口访问底层 RADOS 集群存储系统的各种库函数;
    3. RAODS 提供了一个完整的存储系统,包含:Monitors、OSDs、MDS
        a. Monitor:用来监视Ceph集群的健康状态,并保存各种map信息;
        b. OSDs:主要用来存储数据的守护进程,以PG位单位进行数据的存储、迁移和恢复;
        c. MDS:文件系统的元数据管理服务流程,主要针对CephFS中的元数据信息进行管理。
    4. 在 librados 的基础上又抽象出了块设备库(librbd)、对象存储的库(libgw)和文件系统库(libcephfs)。

 

Ceph IO 流程

此处以CephFS为例简要介绍Ceph 的IO流程。首先由客户端发起IO请求,对提供的文件系统接口的IO请求可以大致分为文件数据IO请求和元数据IO请求。元数据IO请求对应地和元数据管理服务集群进行通信即可,此处重点介绍文件数据的IO处理流程。客户端发起对应的文件IO,调用libcephfs的相关接口,libcephfs作为librados的客户端,会运行简单的HASH路由算法计算此次IO请求对应的PG,再根据CRUSH算法计算出PG对应的OSD组,返回OSD信息给客户端即libcephfs之后,对应地客户端将读写请求发送到OSD上进行处理。而OSD集群中的每一个OSD进程都运行在独立的存储驱动器上,OSD接收到对应的IO请求之后,根据对应的后端存储类型,执行对应后端存储引擎的IO操作,如选择了文件系统作为后端存储引擎,则相应地需要将IO请求转化为文件的读写。

 

 个人理解:
    1. 客户端发起 IO 请求,其 IO 请求发送到服务端分为两种 IO 请求:(1)文件数据IO请求;(2)元数据IO请求
    2. 元数据请求对应的和元数据管理服务集群进行通信即可;
    3. 客户端发起 IO 请求调用 libcephfs相关接口。此时,libcephpfs作为librados的客户端;
    4. libcephfs 进行hash运算出此次的 IO 请求对应的PG,在根据 CRUSH 算法计算出 PG 对应的 OSD 组,并返回OSD 信息给客户端libcephfs
    5. libcephfs将读写请求发送到刚才找到 OSD 组进行处理;
    6. OSD集群中的每个OSD进程都运行在独立的存储驱动器上,OSD 接收到对应的IO请求后,根据对应的后端存储类型,执行对后端存储引擎的IO操作
    7. 如选择了文件系统作为后端存储引擎,则响应的需要将IO请求转为文件的读写。

 

Ceph的核心是分布式对象存储系统RADOS,RADOS可以扩展为数千个OSD,并提供自我管理,自我修复,强一致性保障的副本策略等功能。在客户端侧,Ceph的库文件librados提供了事务性接口,能够对RADOS进行事务性的对象操作、对象集合操作。

 

个人理解:
    1. Ceph 的核心是分布式对象存储系统RADOS,且可扩展为数千个OSD;
    2. Ceph 通过librados 一分为二。

 

 

RADOS中的对象保存在一个逻辑分区——存储池(pool)中。Pool通过多副本或纠删码两种方式实现了数据冗余的功能。在每个pool中,对象被划分到不同的放置组(Placement Group, PG)。在Ceph的数据冗余策略中,数据以PG为单位进行保存,PG是副本划分,数据迁移的最小单位。PG通过一种伪随机的哈希算法——CRUSH算法分配到不同的OSD中,客户端在了解集群的拓扑结构后,可以通过CRUSH算法判定某个对象所在的OSD。PG和CRUSH算法构成了对象到OSD的转换层。而当集群负载发生变化时,RADOS能够提供有效的数据迁移策略,确保高可用和负载均衡。

个人理解:
    1. 在Ceph中,文件被看作对象;
    2. RADOS将对象保存到一个逻辑分区(Pool)中;
    3. Pool 通过多副本和纠删码两种方式来实现数据冗余;
    4. 在每个Pool中,对象被划分到不同的归置组(PG);
    5. Ceph的数据冗余策略中,数据以PG为单位进行保存,PG是副本划分、数据迁移的最小单位;
    6. PG通过一种伪随机算法(CRUSH)分配到不同的OSD 中;
    7. PG和CRUSH算法构成了 对象到OSD 的转换层;
    8. 当集群发生变化时,RADOS能够提供有效的数据迁移策略,确保高可用和负载均衡。

 

在RADOS集群的每个节点上,每块磁盘运行有独立的Ceph OSD守护进程。每个OSD都会处理来自客户端的I/O请求,并且OSD之间也会相互合作,完成数据迁移,副本更新,错误恢复等功能。OSD内部提供抽象接口ObjectStore。ObjectStore是Ceph OSD中最重要的概念之一,它封装了所有对底层存储的IO操作。读请求会通过ObjectStore提供的API获得相应的内容,写请求也会利用ObjectStore提供的事务API将所有写操作组合成一个原子事务提交给ObjectStore。ObjectStore通过接口对上层提供不同的隔离级别,目前PG层只采用了Serializable级别,保证读写的顺序性。
ObjectStore主要接口分为三部分,第一部分是Object的读写操作,类似于POSIX的部分接口,第二部分是Object的属性读写操作,这类操作的特征是kv对并且与某一个Object关联。第三部分是关联Object的kv操作(在Ceph中称为omap)。

对应存储后端需要实现ObjectStore接口,数据最终会调用ObjectStore接口持久化到存储后端。同一RADOS集群可以使用不同种类的存储后端,但在实践中通常统一。

个人理解:
    1. 在RADOS集群中,每块磁盘运行有独立的Ceph OSD 守护进程,每个OSD都会处理来自客户端的I/O请求,且能够相互合作,完成数据迁移、副本更新、错误恢复等;
    2. ObjectStore封装了所有对底层存储的IO操作

 

Ceph 后端存储引擎发展历程

Ceph提供存储功能的核心组件是RADOS集群,其本质是一个提供了大量接口的分布式对象存储集群,最终都是以对象存储的形式对外提供服务。但在底层的内部实现中,Ceph的后端存储引擎在近十年来经历了许多变化。现如今的Ceph系统中仍然提供的后端存储引擎有FileStore、NewStore和BlueStore。但该三种存储引擎都是近年来才提出并设计实现的。

Ceph存储引擎最初的实现为 EBOFS(Extent and B-Tree-based Object File System),其本质也为文件系统,只是在文件系统的基础上做了扩展,主要核心逻辑使用B-Tree来实现,但EBOFS缺少了很多生产环境中的必须的实用性功能,如事务和校验和等,2008年出现并引入了Btrfs之后就摒弃了EOBFS。

Btrfs作为Ceph的后端存储系统经历了长时间的开发,提供了EBOFS不能提供的事务、校验、数据去重等功能,但投入使用很长一段时间后发现Btrfs的碎片化现象比较严重,所以又开始了对新的文件系统存储后端的探索。

在FileStore的存储后端中,一个对象的集合对应一个目录,对象数据保存在文件中。一开始,对象的属性保存在POSIX的扩展文件属性中,但很快,由于文件存储的扩展属性数量和长度限制,Ceph将对象属性转移到LevelDB这类事务性数据库中。基于BtrFS的FileStore成为业界主流并持续了数年,暴露了一些问题。比如BtrFS仍然不稳定,数据和元数据碎片化严重。而与此同时,ObjectStore的接口也发生了很大变化,不能再使用过去的EBOFS实现。为了获得更好的扩容性和元数据处理性能,FileStore迁移到通用文件系统。

在2011年正式使用XFS作为Ceph存储后端之前,Ceph团队还尝试过使用其他文件系统如ext4、ZFS等作为存储后端,但最后选择了XFS,因为其具有更好的伸缩性,元数据的操作性能也较好。

XFS比起Btrfs在运行过程中更为稳定,性能波动较小,但仍让有元数据碎片的问题,无法充分利用硬件设备的性能。同时缺少事务的支持,使得需要实现额外的WAL机制来提供事务功能。Ceph于是提出了新的解决方案来处理元数据碎片化的问题。

Ceph于2015年开发了NewStore,一种键值存储引擎来管理元数据,从而避免元数据碎片带来的问题。通过建立在KV存储中建立和文件系统上的物理文件的索引来向外提供服务,该存储引擎未真正投入生产环境。但该存储引擎奠定了后来且流行至今的BlueStore的基础。

2017年开始在NewStore的基础上提出了BlueStore,使用了NewStore中对元数据的管理方式,但不再使用文件系统存储实际的数据,而直接使用了裸设备来直接存放,从而提升IO的性能。该引擎被无数次实践表明是目前性能最好的后端存储引擎,所以被广泛使用到生产环境中。

 

 

 个人理解:
    1. RADOS 是 Ceph 存储功能的核心组件;
    2. RADOS后端存储引擎:FileStore、NewStore、BlueStore;
    3. 在FileStore的存储后端中, 一个对象的集合对应一个目录,对象数据保存在文件中,为了获得更好的扩容性和元数据处理性能,FileStore迁移到通用文件系统;
    4. FileStore 最好运行在 XFS文件系统之上,因为其有更好的伸缩性,元数据的操作性能更好;
    5. NewStore提供了一种键值存储引擎来管理元数据,该存储引擎未真正投入生产环境;
    6. BlueStore 使用键值存储来对元数据进行管理,不再使用文件系统存储实际数据,直接使用裸设备来直接存放,从而提升IO性能,该引擎是目前性能最好的后端存储引擎。

 

Ceph 典型后端存储引擎介绍

 

FileStore

 

概述

FileStore,也就是利用文件系统的POSIX接口实现ObjectStore API。每个Object在FileStore层会被看成是一个文件,Object的属性会利用文件的xattr属性存取,因为有些文件系统(如Ext4)对xattr的长度有限制,因此超出长度的元数据会被存储在DBObjectMap或omap中。通常DBObjectMap的存储引擎时LevelDB或RocksDB。

个人理解:
在 FileStore中,对象被看作文件,并借助文件系统的接口来进行存储。

 

体系架构

如图所示,开始时,所有写事务的数据和元数据都会先写入Journal文件,写入Journal成功以后立即返回。Journal类似于数据库的write-ahead-log。写入完成后,会使用O_DIRECT和O_DSYNC同步写入到Journal文件,随后该事务会被转移到FileStore的写入队列中,数据和元数据被异步写到指定区域。

 

 

 

补充说明:

问题1:什么是 write-ahead-log?

write-ahead-log 和 append-only-file 的原理

无论是RBDMS 还是 NOSQL ,如何最大程度上保证故障时的数据恢复,都涉及到数据持久化的技术。

write-ahead-log : WAL 日志,是数据库中一种高效的算法。从数据库原理而言,它实现的是redo日志模式。即修改数据库时,不直接修改数据库内容,而是将修改完的数据写入日志同步到磁盘上,这样对其他读进程没有影响。如果数据库崩溃,重启后扫描日志文件,然后更新到数据库中。为了提高效率,WAL日志模式提供了 checkpoint 操作,来定时进行数据更新操作。

append-only-file:AOF日志,以Redis为例。在Redis异常死掉时,最近的数据会丢失(丢失数据的多少视你save策略而定),当业务量很大时,可能丢失的数据会很多。Append-only方法可以做到全部数据不丢失,但Redis的性能就要差些。AOF就可以做到全程持久化,开启AOF之后,Redis每执行一个修改数据的命令,都会把它添加到aof文件中,当Redis重启时,将会读取AOF文件进行“Log-Rewriting 重放”以恢复到Redis关闭前的最后时刻。

 

个人理解:

简单的说,write-ahead-log(WAL)  为了保证数据的一致性,首先将数据写入到日志,然后在异步或者同步写入到数据文件,而checkpoint 的用途就是记录日志文件写入数据文件。

 

  • 首先,为了提高写事务的性能,FileStore增加了fileJournal功能,为了提高写事务的性能,所有的写事务在被FileJournal处理以后都会立即callback(上图中的第2步)。日志是按append only的方式处理的,每次都是被append到journal文件末尾,同时该事务会被塞到FileStore op queue;
  • 接着,FileStore采用多个thread的方式从op queue 这个 thread pool里获取op,然后真正apply事务数据到disk(文件系统pagecache)。当FileStore将事务落到disk上之后,后续的读请求才会继续(上图中的第5步)。
  • 当FileStore完成一个op后,对应的Journal才可以丢弃这部分Journal。对于每一个副本都有这两步操作,先写journal,再写到disk,如果是3副本,就涉及到6次写操作,因此性能上体现不是很好。

 

关键技术

Ceph曾持续很长一段时间,将FileStore作为产品线的存储后端。从当时的历史背景来看,是必然的。FileStore提供了很多优良的特性。

  • 利用了文件和对象天然的映射关系。
  • 利用页缓存机制和inode节点缓存机制作为数据、元数据的缓存。
  • 从软件层面保证磁盘的隔离性。

 

利用了文件和对象天然的映射关系

文件和对象存在天然的映射关系。文件名对应对象名,文件内容对应对象数据,文件的附加属性对应对象的元数据,文件的目录对应对象集合。通过简单的协议转化,FileStore可以利用现有的文件系统快速构建后端存储的基本功能。同时,POSIX接口是linux文件系统的标准,FileStore的转换层和文件系统耦合性很低,可以根据不同的要求,在不同文件系统上快速开发存储后端。

 

利用页缓存和inode缓存

Linux为文件系统提供了页缓存机制和inode缓存机制,能大幅度提升文件的访问速度。而FileStore基于文件系统构建,使用inode存储对象、对象集合的元数据,使用文件存储对象数据,能够直接利用页缓存和inode缓存,管理元数据,提升读写性能。

为了保证事务性,存储系统常常会采用WAL的方法。WAL机制需要先将数据同步到Journal文件,随后可以异步地写入磁盘,因而我们常常需要为Journal设定缓存,管理写入磁盘的数据队列。而使用文件系统避免了去实现另一套缓存方案。

 

从软件层面保证磁盘的隔离性

文件系统地一大优势在于,从软件的层次提供了隔离不同应用数据的方案。这让我们可以更高效的利用磁盘空间。我们可以同时在同一文件系统上运行操作系统,应用软件,以及Ceph的存储后端。这些进程的数据不会相互污染。

 

个人理解:
FileStore 完全基于文件系统建构的存储引擎,借助了诸多文件系统的功能。

 

存在的问题

 

事务机制和性能难以均衡

应用需要存储系统提供事务性保证,但是在FileStore上,事务机制的实现却遇到了种种问题。现有的很多工作提出了文件系统的事务机制,但最终因为高性能开销,功能缺失,接口复杂,实现困难等原因,都不能适应生产环境。

例如,有三类实现事务的方法。

    • 利用文件系统内部的事务机制;
    • 在用户空间实现WAL;
    • 使用WAL机制的数据库。这类实现就是上述提到的NewStore,在本节中不做过多阐述。

一二两种方案都有各自的缺点。

第一类方法。利用文件系统内部的事务机制已经被证明是不合适的。一方面,我们需要进入内核态对文件系统内部进行修改,另一方面,文件系统的事务机制仅能保证文件系统中原子性操作的一致性,并不能保证整个存储后端的一致性。例如Btrfs,第一版的存储后端利用Btrfs的原子性操作接口来完成事务,但是常常如果事务中途发生故障,可能会出现事务部分提交的现象,产生不一致。

第二类方法,在用户空间实现WAL,这也是FileStore在生产环境中使用的方法。

这样的实现方式却面临三个问题。第一是读后写的问题。在进行小块数据写入时,因为块大小的限制,我们需要读取相邻数据进行修改。在连续的写入过程中,因为Journal的操作是严格串行化的,即只有当前一个事务提交成功后,才可以进行读取操作。事务的提交总共包含三个步骤:1)序列化事务并写入日志;2)调用fsync提交事务;3)等待事务操作在文件系统中生效。因为第二步的副作用,下一个事务可能会因为读后写机制阻塞。

第二个问题是操作的非幂等性。这里举一个例子,我们考虑这样一个事务:①克隆a->b,②更新a,③更新c。事务提交后,将日志异步写入后端存储的过程中,如果②③之间发生故障,系统会回滚重新执行操作①,但这个时候对象a已经被更新过了,这将导致不一致性。

第三个问题,重复写。在文件系统上添加WAL机制,直观来说会有两次重复写,一次写入Jounel,一次写入XFS文件系统。同时XFS本身也是一个日志文件系统,在其内部数据会写入两次。因而同一数据可能至少被写入三次。面对这种现象,开发者往往会进行妥协,仅日志化元数据而取消回滚的功能。即先将数据同步写入磁盘,然后将元数据写入日志,执行fsync完成事务提交。但这也会导致极大的同步开销。fsync会调用开销巨大的FLUSH CACHE指令,而如XFS的日志文件系统写入数据时,也会调用上述两次fsync操作,于是单次写入将调用四次fsync。

 

个人理解:
    1. 第一个读后写的问题 和 第二个非幂等性 不是很理解;
    2. 第三个重复写 WAL 机制 首先写入日志,然后在写入数据文件 一共两次,这里写入文件时,是在XFS文件系统上写入的,因此 XFS还会在记录一次,所以一共上三次,造成了多次重复的写入,带来了极大的性能开销;
  3. 使用 FileStore作为后端存储引擎,写入一个文件时,系统会发生两次日志写入,一次数据文件写入,调用四次 fsync

 

低效的元数据管理

本地文件系统中低效的元数据管理始终是分布式系统的心腹大患。在FileStore中,这一问题主要表现为在庞大的目录项中枚举符合条件的目录的效率低下,并且返回的数据没有顺序难以处理。
RADOS中的对象通过hash函数映射到不同的PG中,并且按照hash值的排序进行遍历。在Ceph中,枚举操作是常常发生的,比如在数据去冗余、数据恢复、客户端提取对象列表等操作是,我们需要一一访问相应对象。而很多时候由于上层负载的特性,RADOS中的对象拥有很长的对象名。FileStore因为文件名称长度限制,会将过长的对象名放在文件附加属性中,于是访问这些对象,常常需要调用额外的stat指令获取对象名。以上种种问题导致了元数据遍历十分低效,解决这一问题的一般方法是创建具有大扇区的目录层次结构,将对象分布在目录中,然后在读取后对所选目录的内容进行排序。

为了进行快速排序,减少额外的stat调用损耗,文件夹会保持较少的子文件数(一般是数百个)。当子文件夹数量增加时,会采取分割文件夹的放置维持数量。但这导致极大的并行性开销,首先,会占用大量目录项缓存,并产生大量磁盘小I/O;其次,XFS采用分配组的方式,让新的目录项与原有目录项相邻,导致分割文件夹使会浪费许多时间用于寻找对应空间。以上两个原因,影响了分割文件夹时的I/O性能。

 

个人理解:
这段对于 FileStore 元数据管理的描述,不是很理解。

总结:
FileStore 优势:
利用成熟的文件系统能够快速的实现存储引擎的构建
FileStore劣势:
在写入数据前需要先写入Journal,然后在写入磁盘,带来了翻倍的性能消耗

 

NewStore

概述

为了解决上述FileStore存在的问题,Ceph引入了新的存储引擎NewStore(又被称为Key-File Store)。通过将元数据从传统的文件系统上分离出来,使用专门的KV数据库如LevelDB或RocksDB来进行管理,而对应的对象数据依旧保存在文件系统中,从而避免文件系统作为后端存储引擎时的元数据碎片化问题,从而提升Ceph的整体性能。

个人理解:
    1. NewStore相比FileStore来说,将元数据从传统的文件系统上分离出来利用专门的KV数据库进行管理;
    2. 对于对象数据依旧保存在文件系统中,避免了FileStore文件系统元数据碎片化问题。

 

体系架构

NewStore的关键数据结构和KV数据分布如图所示。NewStore首先将Key的相关信息、对象的元数据信息以及对象的值的地址信息都统一进行了封装,以KV的形式存储在KV数据库中,而对象的值的信息则沿用文件系统的方式进行存储,相比于文件系统,添加了一些映射信息来将KV数据库中存取的元数据信息和存储在文件系统中的对象信息进行物理映射。图中的保存在KV数据库中的ONode即为索引结构,建立Obejct和存储在文件系统中的物理文件之间的映射关系。ONode又主要包含对象的扩展属性信息和存储了物理文件地址的分段信息。Object和Fragment的对应关系取决于对象的大小,所以很有可能一个Object对应多个Fragment,或者多个Object对应多个Fragment(当对象较大时,由于Fragment文件大小限制,所以需要你多个文件共同构成,当对象较小时,为了不浪费Fragment文件的空间,可以多个小对象对应一个Fragment文件)。为了较好地处理这两种情况,所以需要根据Fragment所在的物理地址(即偏移量)和Fragment的数据长度作为Fragment对象的属性,同时还包含FID对象,FID又主要包含了物理文件所在的目录信息即fset和文件编号fno,从而能够在文件系统中正确定位到真正的物理文件。

 

 

个人理解:
NewStore 将 元数据 和 对象数据分离存放,元数据等信息则上通过键值对的形式存储在KV数据库的,而对象的值则还是和FileStore一样以文件的形式存储在文件系统中的。

 

关键技术

    1. 解耦对象与本地物理文件间的一一对应关系,通过索引结构(上图中ONode)在对象和本地物理文件建立映射关系,并使用KV数据库存储索引数据信息,从而减小元数据碎片;
    2. 在保证事务特性的同时,对于对象的创建/追加写/覆盖写(与分段文件大小对齐)操作,无需Journal支持,减小了写放大;
    3. 对于非对齐的数据更新操作,先同步写入write-ahead-log文件中(简称为WAL,使用KV存储),再异步写入相应的分段(fragement)文件;
    4. 可以在KV数据库上层建立Onode数据缓存以加速索引数据信息的读取操作;
    5. 单个对象可以有多个分段(fragement)文件,多个对象也可共存于一个分段(fragement)文件,从而能够有效提升分段(fragement)文件的空间利用率,减小数据碎片的产生;
    6. 为了减小WAL的开销,在数据修改操作同步到分段(fragement)文件中后,立即将WAL日志从KV数据库中删除,同时增大KV数据的写缓冲区,尽量将WAL保存在缓冲区中,从而减少dump操作,在dump操作执行前,合并多个缓冲区的数据,减少dump的次数。

存在的问题

NewStore存在的问题和FileStore相似,因为NewStore沿用了FileStore存储对象数据的方式,只是将对象的元数据信息和对象数据进行了分离,引入KV数据库优化了元数据管理,但在文件系统层面仍然存在严峻的写放大问题。由于对象数据的最终形式都是以文件的形式存储在文件系统中,文件系统相关的保障机制不可避免地产生了写放大问题。即便是存储在KV数据库中的元数据信息和对象扩展数据信息,由于KV数据库仍然运行在传统的文件系统上,最终的存储流程仍然不能绕过文件系统的层次,所以文件系统引入的高开销问题仍然不能被有效解决。但NewStore的提出和研究也为后来的BlueStore奠定了基础。

 

BlueStore

概述

在经历了FileStore的发展和应用之后,逐渐意识到元数据管理和写放大问题成为了限制Ceph存储性能的重要原因。在对NewStore进行了设计和研究的基础之上,发现元数据的管理可以采用KV数据库进行存取,同时将Key和Value进行解耦,但仍然避免不了对象数据存储在文件系统上的开销。于是Ceph官方团队逐渐意识到,其真正限制后端存储引擎性能的根本原因是文件系统本身。之所以一开始采用文件系统,得益于文件系统在过去的存储系统中应用广泛,且经受了住许多考验,文件系统的稳定性使文件系统成为了现如今绝大多数存储系统存储后端的首选。但随着Ceph的不断发展,意识到必须要突破文件系统的限制才能在性能上有比较大的突破,所以Ceph研发了新的基于裸设备的存储引擎BlueStore。

 

个人理解:
    1. 元数据管理和写放大是Ceph 存储性能的痛点;
    2. FileStore之所以采用文件系统作为后端存储得利于文件系统的稳定和广泛使用;
    3. BlueStore是基于裸设备的存储引擎。

 

BlueStore是在NewStore的基础之上,将对象数据的存放方式改为直接对裸设备进行指定地址和长度的读写操作,不再依赖文件系统提供的POSIX接口。对于元数据的管理则沿用NewStore中的方式,使用KV数据库来对元数据进行管理,但KV数据库无法直接运行在裸盘上,必须运行在文件系统的基础之上,为了避免传统文件系统引入的开销,BlueStore对文件系统进行了定制,实现了一个自定义的小型的文件系统BlueFS,其KV数据库则运行在BlueFS的基础之上,从而提供元数据管理的相关功能。

 

个人理解:
上面这段话对 BlueStore 下了一个定义:
对象数据存储方式:BlueStore是在NewStore的基础之上,将对象数据的存放方式改为直接对裸设备进行指定地址和长度的读写操作,不再依赖文件系统提供的POSIX接口。
元数据存储方式:KV数据库无法直接运行在裸盘上,必须运行在文件系统的基础之上,为了避免传统文件系统引入的开销,BlueStore对文件系统进行了定制,实现了一个自定义的小型的文件系统BlueFS,其KV数据库则运行在BlueFS的基础之上,从而提供元数据管理的相关功能。

 

体系架构

BlueStore整体架构分为三个部分:BlockDevice、BlueFS和RocksDB。

BlockDevice为最底层的块设备,通常为HDD或者SSD,BlueStore直接操作块设备,抛弃了XFS等本地文件系统。BlockDevice在用户态直接以linux系统实现的AIO直接操作块设备,由于操作系统支持的aio操作只支持directIO,所以对BlockDevice的写操作直接写入磁盘,并且需要按照page对齐。

BlueFS是一个小的文件系统,其文件系统的文件和目录的元数据都保存在全部缓存在内存中,持久化保存在文件系统的日志文件中, 当文件系统重新mount时,重新replay该日志文件中保存的操作,就可以加载所有的元数据到内存中。其数据和日志文件都直接保存在依赖底层的BlockDevice中。主要还实现了RocksDB:Env所需要的接口。BlueFS在设计上支持把.log和.sst分开存储,.log使用速度更快的存储介质(NVME等),从而提高WAL日志的性能。

RocksDB 是Facebook在leveldb上开发并优化的KV存储系统。本身是基于文件系统的,不是直接操作裸设备。它将系统相关的处理抽象成Env,用户可实现相应的接口。BlueFS的主要的目的,就是支持RocksDB Env接口,保证RocksDB的正常运行。

BlueStore是最终基于RocksDB和BlockDevice实现的Ceph的对象存储,其所有的元数据都保存在RocksDB这个KV存储系统中,包括对象的集合,对象,存储池的omap信息,磁盘空间分配记录等都保存RocksDB里, 其对象的数据直接保存在BlockDevice上,不使用本地文件系统,直接接管裸设备,并且只使用一个原始分区。

个人理解:

对 BlueStore 的理解需要分为三个部分:BlockDevice 、BlueFS 和 RocksDB

BlockDevice 通常指的就是硬盘,对BlockDevice的写操作直接写入硬盘,摒弃了文件系统的限制;

BlueFS 是一个小的文件系统,主要是为了支撑实现 RocksDB而诞生的,其文件系统和目录数据都保存在内存中,持久化到文件系统的日志文件,其数据和日志文件直接保存到底层的 BlueStore中,BlueFS 通常建议使用速度更快的存储介质,从而提高WAL日志的性能。

RocksDB 主要是对对象元数据进行KV存储,本身基于文件系统,不是直接操作裸设备。

BlueStore是最终基于RocksDB和BlockDevice实现的Ceph的对象存储,其所有的元数据都保存在RocksDB这个KV存储系统中,包括对象的集合,对象,存储池的omap信息,磁盘空间分配记录等都保存RocksDB里, 其对象的数据直接保存在BlockDevice上,不使用本地文件系统,直接接管裸设备,并且只使用一个原始分区。

 

关键技术

快速的元数据操作:通过在RocksDB中存储元数据,BlueStore实现了快速元数据操作。

RocksDB运行在BlueFS上,而BlueFS是在用户空间实现的基于区段和日志记录的高度定制化文件系统,实现了RocksDB运行必需的Env相关接口,实现了RocskDB要求的基础系统调用,BlueFS会为每一个文件维护一个inode,其中包括分配给该文件的区段列表,类似与NewStore中的机制。超级块存储在确定的物理地址上,且包含了日志的inode信息,其文件系统的文件和目录的元数据都保存在全部缓存在内存中,持久化保存在文件系统的日志文件中, 当文件系统重新mount时,重新replay该日志文件中保存的操作,就可以加载所有的元数据到内存中。当日志大小达到阈值时,日志文件将被压缩并写到一个新的日志文件中,同时将日志的新地址信息记录到超级块中。其BlueFS的架构如图3-5所示。

 

 

 

 

减小了写放大:首先,它直接将数据写入原始磁盘,从而只产生一个用于数据写入的缓存刷新操作。其次,BlueStore改变了RocksDB的机制,重用WAL日志文件作为一个循环缓冲区,从而为元数据的写入也只产生一个缓存刷新的操作。缓存刷新操作的减少直接决定了开销较大的同步系统调用操作减少,从而优化了在文件系统中存在的多次缓存刷新操作带来的效率问题。

CoW机制保证了快速的克隆操作:BlueStore支持CoW机制,CoW(Copy-On-Write)是指当覆盖写发生时,不是更新磁盘对应位置已有的内容,而是新分配一块空间,写入本次更新的内容,然后更新对应的地址指针,最后释放原有数据对应的磁盘空间。对于数据大小大于最小分配块的写操作(HDD最小分配块大小为64 KiB,SSD最小分配块大小为16KiB),数据将被写到一个新分配的数据块内。一旦数据被持久化,相应的元数据就被插入到RocksDB中。这使得BlueStore能够提供快速的克隆操作,克隆操作只是增加所属区段的引用计数,并将写操作定向到新的区段。

没有日志双写问题:由于BlueStore的对象数据的写流程中没有了文件系统的参与,对象数据都是直接写入到底层块设备中,从而避免了文件系统中广泛存在的日志双写问题,只是在元数据的IO过程中仍然要使用WAL机制来进行保证,但又由于引入了CoW机制,使得需要WAL机制保证的IO数据大小大大减小,从而减小了日志双写问题带来的影响。

增加数据校验及数据压缩等功能: BlueStore 为每次写操作计算校验和,为每次读操作验证校验和,也支持多种校验和算法。CRC32c 由于其在 x86 和 ARM 架构上都有比较好的优化被用作默认校验和算法,在检测随机Bit位错误时也很高效。由于BlueStore 完全控制了整个 IO 栈,BlueStore 可以根据 IO 的情况决定计算校验和的大小。

能够对新型存储器件提供更好的支持:由于传统的文件系统作为存储后端,更多的是支持块设备等物理存储器件,而现如今出现的新型的存储设备,如SMR SSD和NVM等新型存储器件,不再如以往的块设备一样对提供块接口,在文件系统没有做到真正的适配之前,BlueStore可以充分利用自己裸盘读写的特点,不受文件系统的限制,直接操纵裸设备。

 

个人理解:

BlueStore 采用的关键技术:

快速的元数据操作:通过在RocksDB中存储元数据,BlueStore实现了快速元数据操作;

减少了写放大,在FileStore中基于文件系统的缓存刷新至少是三次, 但是这里采用裸设备,缓存刷新减少为1次;

CoW机制保证了快速的克隆操作,指的是 当发生覆盖写时,不会对源数据进行修改操作,而是新增一条,然后修改地址指针;

没有日志双写问题:是因为BlueStore的对象数据的写流程没有文件系统的参与;

增加数据校验及数据压缩等功能 - 不太明白用途;

能够对新型存储器件提供更好的支持:不在受限于文件系统的限制,可对裸设备直接操作。

 

补充:

BlueStore 的数据读写

BlueStore的数据写入分为两类:

1. 数据是整块覆盖写,对于这一类写入:

  重新分配新的存储空间;

  把数据写入新分配的存储空间;

  删除旧的存储空间。

 

2. partial write,在这种情况下,部分块的写入,在这种情况下:

  overly write

  wal write

  这两种方式都是先把数据写入到 KV 存储中,后续再 apply 到实际的存储空间中,不同之处在于触发条件不同。

 

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

BlueStore 优点在于:

  对于整块数据的写入,数据直接 AIO 的方式写入磁盘,避免了 FileStore 的先写日志,后 apply 到实际磁盘的两次写盘;

  对于随机 IO, 直接 WAL 的形式,直接写入 RocksDB 高性能的 KV 存储中。

 

存在的问题

BlueStore在自己实现读裸设备管理的核心思想下,就不得不面对很多机制都需要自己来实现优化的场景,面临的问题主要表现在以下三个方面:

缓存大小的调整和写回策略的实现:由于BlueStore抛弃了文件系统作为实际的存储形式,所以不可避免地就需要自己是现在在文件系统中表现优异的各类缓存机制和策略。文件系统继承了操作系统页缓存的优势,会根据应用程序的使用情况动态调整缓存的大小;而对于类似于 BlueStore 穿过内核的存储后端需要自己实现类似的机制。BlueStore 中需要自己手动设置缓存大小相关的参数,如何在用户态构建一个如操作系统页缓存动态调整大小的机制时很多存储系统都面临的问题,如 PostgreSQl,RocksDB。同时面对已经出现的 NVMe SSD,缓存需要更加高效,才能减小 SSD 的写负载,同时也是当前页缓存面临的问题。

KV 存储的效率问题::Ceph 的经验表明把所有元数据给移植到有序的 KV 存储(如 RocksDB)上能够显著提高元数据操作的性能,然而同时也发现嵌入 RocksDB 带来的一些问题:Ceph 的经验表明把所有元数据给移植到有序的 KV 存储(如 RocksDB)上能够显著提高元数据操作的性能,然而同时也发现嵌入 RocksDB 带来的一些问题:

在 OSD 节点上使用 NVMe SSD 时,RocksDB 的压缩机制和严峻的写放大问题已经成为了主要的性能限制;

由于RockDB被视为一个黑盒,因此数据被序列化,并在其中来回复制,消耗数据 CPU 时间;

RocksDB 有自己的线程模型,这限制了自定义分片的能力。

 

CPU和内存的效率问题:Ceph 中复杂的数据结构,且生命周期较长,采用默认的布局一定程度上会造成内存的浪费。因为现代编译器在内存中对齐和填充基本数据类型,这样CPU就可以方便地获取数据,从而提高了性能。然而跨越了内核的存储后端控制几乎机器的所有的内存,内存的优化和隔离机制需要自己动手实现;如上提到的 KV 存储的问题,在使用 NVMe SSD 时,其工作负载会被 CPU 限制,涉及到大量的序列化和反序列化操作。故 Ceph 开发团队试图减小 CPU 的消耗,通过减小序列化和反序列化数据的大小,尝试使用 SeaStar 框架的共享模型来避免由于锁导致的上下文切换。

 

总结

Ceph的后端存储引擎随着日新月异的需求的变化不断地向前发展,但发展的大体脉络也基本揭示了了分布式存储系统的变革规律。从一开始选择使用功能稳定、机制成熟、且代码久经考验的文件系统作为存储后端,更多的是出于功能稳定性上的考虑,但随着场景的复杂化,数据规模的不断增大,不同的IO类型的出现,以及实际生产环境中对性能的极致要求,以Ceph为代表的分布式存储系统的经验表明文件系统不再适用于如今的分布式存储场景,其性能的低下和开销的巨大使得开发者们不得不在文件系统的基础上进行优化。但随着近年来新型存储器件技术的发展,使得存储设备对外提供的接口多样化,不再局限于传统的块接口,而基于传统的块设备设计的文件系统无法对新型存储器件进行较好的适配。在对性能的极致要求和新型存储器件提出的挑战的共同作用下,Ceph开始了新型存储后端引擎的研究,打破了文件系统作为存储后端的垄断格局,并结合其他后端存储引擎的优势如KV存储等,设计了一种新的基于裸设备的管理方案,并定制化小型文件系统来对传统的依赖POSIX接口应用进行适配,充分利用存储器件的性能,尽可能地降低软件的开销,将存储引擎的职责单一化专门化,从而更好地为分布式存储系统提供相应的服务和功能。

如今BlueStore已经成为了Ceph用户的后端存储引擎第一选择,BlueStore带来的性能上其他存储后端不可比拟的优势和对新型存储器件的支持使得BlueStore已经被应用到绝大多数场景。现在也已经成为了Ceph默认使用的存储引擎后端。BlueStore解决了FileStore的许多缺陷的同时提升了相应的写性能,同时在读性能方面没有损失,也逐渐在适应变化的需求和应用场景,增加了诸如数据校验、数据压缩等新特性。BlueStore虽然整体表现优异,但在主打机械磁盘的传统阵列中的表现依旧差强人意,小粒度的读写性能相比于文件系统作为存储后端提升相对有限,因此针对BlueStore的性能优化依然任重而道远。BlueStore解决了FileStore的许多缺陷的同时提升了相应的写性能,同时在读性能方面没有损失,也逐渐在适应变化的需求和应用场景,增加了诸如数据校验、数据压缩等新特性。BlueStore虽然整体表现优异,但在主打机械磁盘的传统阵列中的表现依旧差强人意,小粒度的读写性能相比于文件系统作为存储后端提升相对有限,因此针对BlueStore的性能优化依然任重而道远。

 

展望

通过Ceph后端引擎的发展历程,我们不难发现,传统的文件系统存储后端在不断完善相关功能如事务、克隆和快照等操作的同时,也在进行转型。通过融合其他存储引擎的优势共同组成一个存储系统来对外提供服务,诸如元数据管理更多的是交给KV存储来进行处理,充分利用其他存储引擎所长,并将各自进行组件化,模块化,以模块的形式和其他存储模块进行融合,共同构成高效的存储系统。后来产生的BlueStore正是存储引擎模块化组件化的成果,通过充分发挥各类存储引擎的优势,将KV存储引擎用于元数据的管理,将文件系统进行定制,根据实际的场景减小文件系统的功能复杂程度,定制了BlueFS,同时也有开天辟地的新型存储引擎的设计与实现,如基于裸设备的存储,通过将各类存储引擎组件化,可以根据实际的需求将组件进行整合,从而构成一个与需求紧密结合的存储系统。未来存储系统的组件化将应用更加广泛,尤其是针对现如今不断发展的存储器件技术,传统的基于块设备的存储引擎势必需要对新型存储器件进行适配,组件化存储引擎之后可以充分减小适配的工作量,同时对于未来可能出现的新技术也能较好地进行吸收和整合。BlueStore的出现开启了一段存储系统变革的新纪元,未来面向实际应用场景的定制化存储系统可能才能真正充分发挥存储系统的性能。

 

参考链接:

https://zhuanlan.zhihu.com/p/101772382

posted @ 2020-04-07 13:57  hukey  阅读(3328)  评论(0编辑  收藏  举报