HDFS对象存储--Ozone架构设计

前言


如今做云存储的公司非常多,举2个比較典型的AWS的S3和阿里云.他们都提供了一个叫做对象存储的服务,就是目标数据是从Object中进行读写的,然后能够通过key来获取相应的Object,就是所谓的key-object的存储.这种优点就在于用户使用起来非常方便的,不须要走冗杂的操作流程.可是本文所要阐述的则是HDFS中的对象存储,对于这种需求,Hadoop作为一套完好的分布式系统,当然也要与时俱进,在HDFS-7240中进行了实现,眼下此功能真在开发中,名叫Ozone,内部有非常多的概念与业界的都是相似的,比如Bucket,Object等.下面是本人对于Ozone设计文档的一个译文,可能会有翻译不到位的地方,附上原文链接:Ozone-architecture-v1.pdf

文件夹


  1. 介绍
    1.1 基本要求
    1.2 大小要求

  2. 高层次设计
    2.1 与HDFS共享Datanode Storage存储
    2.2 Storage Container存储容器
    2.3 Storage Container Identifier存储容器标志符
    2.4 Storage Container存储容器服务的调用
    2.5 Datanode中的Ozone Handler
    2.6 Storage Container Manager存储容器管理器

  3. 详细实现
    3.1 对象键值到存储容器的映射
    3.2 范围分区Vs哈希分区
    3.3 bucket到存储容器的映射
    3.4 Storage Container存储容器的要求
    3.5 Storage Container存储容器实现要点
    3.6 Pipeline数据一致性

  4. 未来工作
    4.1 Ozone API
    4.2 集群层级API
    4.3 Storage Volumes层级API
    4.4 Bucket层级API
    4.5 Object层级API

  5. 引用

介绍


Ozone提供了一个key-object键值-对象存储的服务,相似AWS的S3服务. Key和Object对象是随机的字节数组.单个key的大小期望是小于1k,可是values对象能够从小到几百字节大到几百兆.键值/对象被组织进了bucket的概念中.一个bucket拥有唯一的key集合.Bucket存在于Storage-Volume中,而且在Storage-Volume中拥有唯一的名字.一个storage volume拥有全局唯一的名字.更近一步的说,storage volume对buckets和存储的数据有配额上的限制.在私有云中,它能够被用来创建给用户(比如home文件夹),project项目,或者租有者.管理员能够分配一定配额限制的私有storage volume给个体用户或者公有的共享storage volume.在公有云中,多storage volume能够以独立的配额分配在每一个云中.
一个bucket被2部分名字的组合唯一标识: storage-volumeName/bucketName.一个对象的名字被storage-volumeName/bucketName/objectKey 3部分所标识.
我们的模式相似于Azure Blob Storage(WASB).他们的bucket在每一个帐户中是唯一的.可是,我们用storage volume来代替了这个帐户概念.因此相比之下,S3的bucket在全部帐号中保持唯一.
数据的组织结构能够下面面的图形进行展示.
这里写图片描写叙述

基本要求


Ozone中主要的操作例如以下(API方法在文档尾部将会给出):

  • 管理员创建Storage Volume.
  • 创建/删除 buckets.每一个bucket拥有一个独立的URL.Buckets不能被重命名.仅仅有storage volume的所属者或所属组才干创建/删除volume中的bucket.
  • 在一个storage volume中列出buckets列表.
  • 依据给定的key在bucket中创建/删除object对象.对象的数据或值能够流式的传输到Ozone服务中.当对象被写满的时候将仅仅会同意读操作.同一时候不保证对象的局部写.
  • 列举出bucket的内容
  • 创建/更新/删除 buckets的ACL訪问控制列表

大小要求


本节列举了少部分的限制值来详细化一些要求.下面是目标版本号1的一些指标:

  • Storage Volume名字: 3~64字节
  • Bucket 名称: 3~64字节
  • Key大小: 1KB
  • Object大小: 5G
  • 系统总buckets数量: 1000w
  • 每一个bucket中对象数量: 100w
  • 每一个storage volume中的bucket数: 1000

Ozone元数据包括下面内容:

  • Storage Volume层级元数据
    • Storage volume所属者
    • Storage Volume名称
  • Bucket层级元数据
    • Bucket所属者: 拥有此bucket中数据的所属用户.
    • Bucket ACL訪问控制信息
    • 全局唯一的Bucket名称
    • Bucket Id: 与bucket相关的系统产生的数字型的标识

高层级设计


与HDFS共享Datanode数据存储


DataNode同一时候为HDFS和Ozone存储数据.Ozone的数据用的是独立的blockPool并拥有独立的blockPool Id.也就是说能够同一时候存在多种volume命名空间,每一个volume有属于自己的block pool.相同的能够同一时候存在多个独立的Ozone命名空间在所属的block pool下.一个DN能够同一时候存储多个HDFS和Ozone的block pool.此关系结构例如以下图所看到的:
这里写图片描写叙述

Storage Container容器


一个storage container从概念上来说,指的是用来存储Ozone数据(就是bucket中的数据)和Ozone元数据的一个存储单元.Storage container与HDFS的block一样,共同存储于DataNode上.可是与HDFS不同的一点,Ozone没有一个相似于NameNode的中心节点,相反地,他是一个分离的元数据的存储.这些元数据分布式的存在于各个storage container中.每一个storage container以whole的形式存在(相似于HDFS中的block).我们对container副本的成功操作保证了强一致性.Storage container的最大值的大小取决于他的副本复制能力以及从节点故障中恢复的能力.最大值的大小是可配的,可是他至少要大于一个简单对象所同意的最大值.

一个bucket能够拥有百万数量级的对象而且在存储的大小级别上能够达到T级别,这远远大于一个storage container.因此一个bucket会被分为非常多partion分区,每片分区会存储在一个container中.(一个storage container能够包括最大值数量的的分区,然而对象仅仅能来自一个bucket.)在我们的初始设计实现中,一个object对象是全然存在于一个单一的container中,这么做后面可能会轻松一些.

Storage container是靠DataNode来实现的(用户能够通过配置来禁用此功能假设用户仅仅须要使用HDFS的block功能).本节定义了storage container的一些语义和要求.Storage container须要存储下面多种类型的数据,每类数据有稍微不同的语义:

  • Bucket元数据

    • 个体单元的数据是非常小的-kb级别.每一个storage container能够保存上百万bucket的元数据.
    • 这点要求能够更新,由于一个bucket的ACL是能够被更新的,而且要能保证操作的原子性.
    • 满足主要的Get/Put的API设计已经足够.
    • 在container中能运行列表操作以便展示全部的bucket.
  • Bucket数据

    • Storage container须要能够存储小到几百kb大到上百mb的对象数据.
    • 对象须要通过对象的key进行訪问(所以container存储数据也必须包括存储对象的索引).
    • 必须能支持个体对象读写的流式API.
    • 数据中的对象不支持追加写以及原地更新.

在后面的小节,我们将阐述DataNode怎样实现storage container来达到以上的要求.

Storage Container标识符


每一个storage container都被独立的storage container标识符所独立标识.它是一个逻辑上的独立标识(相似于HDFS中的blockId)而且不包括真正意义上container的网络位置.

对象的key会映射到storage container标识符.这个标识符会传入storage container manager管理器去定位包括目标对象container所在的DataNode.相似的,bucket的名称也会映射到storage container标识符,这个container保存有bucket的元数据.在后面的小节中,我们会详细提到这个映射关系是怎样实现的.Storage container标识符是64位的,相似于hdfs中的block id.在未来,我们将把它扩展到128位.可是那将会是一个大的改变,由于我们想尽可能的复用hdfs的block管理器相关的代码.

Storage Container Service中的过程调用


下面的图形展示了Ozone中的典型过程调用,当中主要包括了Storage Container Service服务和Ozone Handler处理器:
这里写图片描写叙述

DataNode中的Ozone Handler


Ozone handler是Ozone中的模块组件,被DataNode所持有并对外提供Ozone服务.Handler包括了一个http server并实现了Ozone REST方式API.Ozone Handler与storage container manager管理器交互来查询container的位置.与DataNode中的storage container交互实现不同的操作.这个功能组件能够被禁用假设用户仅仅想要使用HDFS而不须要Ozone功能.

Storage Container Manager管理器


Storage container manager管理器非常相似于HDFS中block manager中的管理功能.Storage container manager管理器从各个DataNode中收集心跳,处理storage container的报告并跟踪每一个storage container的位置.他包括了一个storage container映射图,以此提供了前缀匹配的方式去查询storage container.之前提到过DN能够同一时候存储HDFS和Ozone的数据.Ozone的数据所属的是分离的block pool而且用的是Storage container manager管理器提供的分离的blockPool Id.图形展示效果例如以下.我们计划尽可能的复用NameNode已有的block管理部分的代码实现来实现container的管理.我们相同能够复用DataNode 中block pool service服务的实现代码.
这里写图片描写叙述

详细实现


映射object-key到storage container

在我们的设计中,我们计划使用哈希分区的模式来映射key到他所存储的storage container.Key的值是被哈希计算的,同一时候会带上bucketId的前缀.结果值通过前缀匹配能够映射到storage container.当container逐渐变大,我们会对此进行切割,并用扩展哈希算法又一次映射key到相应container的映射关系.Storage container manager管理器会存储这些映射关系到前缀树中以此实现高效的前缀匹配.每一个bucket都有属于自己的前缀树.

范围分区 vs 哈希分区


在数据库和对象存储中,范围分区是另外一项比較受欢迎的用来对key进行分区的技术.下面是2个技术的简短的比較.我们偏向于使用哈希分区的方法,由于它相比較能更简单的去实现而且能满足我们的需求.

  • 范围分区模式须要范围索引.范围索引相同非常巨大而且须要分布式的存储.这复杂化了切割多个分区的操作,由于他额外须要索引的更新.
  • 范围分区在搜索过程中多引进了一种额外的跳跃,当第一次范围索引须要去读.
  • 范围分区相同有热点问题.相似名字的key会造成相同范围内的大并发量的訪问.
  • 然而,范围分区提供了有序訪问以及有序列表展示的优势.可是我们觉得对于一个对象存储而言,一个有序的訪问并非一个重要的要求.列表有序的展示能够在后面的阶段内用二级索引的方式来实现.我们相同相信对于眼下的设计而言是足够灵活的,假设我们在未来想要对此加入范围索引的话.

映射bucket到storage container


Bucket的元数据相同存储在storage container中.Bucket的名字是被用来作为key做哈希计算的.多个bucket的元数据能够存在于storage container中.我们在全部与storage container关联的bucket中设计了一个特殊的bucketId,bucketId前缀中会带上container Id.

Storage Container要求


Storage Container存储在各个DataNode上.对于Storage Container我们有下面的要求:

  • Storage Container能够可靠地被复制.
  • Storage Container保持严格的一致性.
  • Storage Container对于内部存储的对象能提供高效的键值对的查询方式.
  • Storage Container必须能够以流式的方式对object进行读写操作.
  • Storage Container必须能够支持get/put接口,来存储和更新bucket元数据.
  • Storage Container对于内部存储的bucket必须能提供一个原子的更新操作.
  • Storage Container能够进行分裂当他们达到一定限制大小的时候.

Storage Container的实现要点


  • 我们倾向于尽可能的复用HDFS现有的block pool管理方面的功能代码.因此我们会将StorageContainer作为Block类的扩展类.一个hdfs block由一个标识符,生成时间记录和大小组成.这3个属性相同能够应用到storage container中.
  • 为了保证一致性和持久性,storage container实现了少量的原子性和持久化的操作,比方事务.Container对这些操作提供了可靠的保证.在第一阶段中,我们实现下面事务操作:
    • Commit: 这个操作促进了对象从被写到最后的确认结束.一旦这个操作成功,这个container就会保证对象是可读的.
    • Put: 这个操作适用于小规模的写操作,比如元数据的写动作.
    • Delete: 删除对象.
  • Container中的每一个事务会有一个事务ID,而且必须被持久化.
  • 我们计划为storage container实现一个新的data-pipeline,由于他要求一个不同类型的更新和恢复语义操作.在下一小节中我们会给出data-pipeline的设计概述.
  • 我们正在考虑利用leveldbjni来作为storage container的原型设计,leveldbjni正好能满足我们storgae container键值对存储的需求.

Data Pipeline一致性

Data-Pipeline管道链流式复制副本数据到container中.Container的副本也有产生记录相似于HDFS的block.每一个pipeline副本的标记记录在pipeline创建的时候会被更新,所以不论什么旧的container会被撤销.HDFS在block恢复的时候额外使用了block length副本长度来推断副本是否已经更新到最新.相似地, storage container用了事务ID来推断container副本是最新的.很多其它的pipeline设计细节将会上传到相应hdfs jira上.

未来工作


我们并没有在文档中细致阐述下述的要求,可是我们将会在后面阶段的工作中实现这些功能:

  • High availability: 高可用性,Storage container manager管理器须要是高可用的服务.一个解决方式是利用HDFS Journal的QJM机制实现Active/Standby的方式.第二种方式做成全部Active的服务配上Paxos环的方式.我们会在第二阶段的工作中对这个问题进行社区内的讨论.
  • Security: 安全,我们能够利用HDFS的kerberos认证机制
  • Cross cool replication: 跨类型副本拷贝,我们将会使用一种对HDFS和Ozone都有利的方式去实现这个功能.

Ozone API


Cluster层级APIs

  • PUT StorageVolumes
    • API - PUT /admin/volume/{StorageVolume}
      • 创建一个storage volume
      • 仅仅有管理员才同意调用这个操作
  • HEAD StorageVolume
    • API - HEAD /admin/volume/{StorageVolume}
      • 检測Storage Volume是否存在
      • 仅仅有管理员才干调用这个操作
  • GET
    • 列出集群中全部的Storage Volume.
  • DELETE Storage Volumes
    • API - DELETE /amin/volume/{StorageVolume}
      • 删除一个volume假设他是空的

Storage Volume层级APIs


  • GET Buckets
  • API - GET /
    • 利用用户的认证信息登录
    • 返回请求发送者所拥有的buckets列表
  • Get User Buckets
    • API - GET /admin/user/userid
      • 利用用户认证信息进行登录, 假设他/她有权限阅读其它用户的信息,返回那个用户所独有的buckets列表信息.

Bucket层级API

  • LIST objects in a bucket
    • API - GET/{bucketName}
      • 返回buckets中最多1000个数量的对象key
  • PUT bucket
    • API - PUT/{bucketName}
      • 为请求发送者创建bucket
    • GET/PUT Bucket ACL
      • API - /{Bucket}?acl
      • 同意用户获取/设置bucket的ACL
  • HEAD bucket
    • API - HEAD/{bucketName}
    • 检查bucket是否存在,前提是请求发送者有权限訪问此bucket
  • DELETE bucket
    • API - DELETE/{bucketName}
      • 删除bucket假设此bucket为空的话

Object层级APIs

  • GET object
    • API - GET/{bucketName}/{key}
      • 返回给定key所代表的对象值,假设这个值存在
      • 在第一阶段暂不支持ACL,因此仅仅有所属用户才干读写自身的bucket.
  • PUT object
    • API - PUT/{bucketName}/{key}
      • 在bucket中创建一个对象
      • 在第一阶段暂不支持ACL,因此仅仅有所属用户才干读写自身的bucket.
      • 不支持局部的上传,仅仅有对象全部上传成功了才被觉得是一次成功的操作.
  • HEAD object
    • API - HEAD/{bucketName}/{key}
      • 检測对象是否存在
      • 在第一阶段仅仅有bucket的所属用户才干调用此操作
  • DELETE object
    • API - DELETE/{bucketName}/{key}
      • 删除对象

引用


1.Extendible Hashing(扩展哈希算法)-
http://en.wikipedia.org/wiki/Extendible_hashing
2.leveldbjni - A Java Native Interface(Java本地接口) to
LevelDB. https://github.com/fusesource/leveldbjn

posted @ 2017-08-14 09:26  zsychanpin  阅读(6272)  评论(1编辑  收藏  举报