如何基于 Ceph 攻破海量小文件ceph 存储视频难题

海量小文件存储与Ceph实践
  海量小文件存储(简称LOSF,lots of small files)出现后,就一直是业界的难题,众多博文(如)对此问题进行了阐述与分析,许多互联网公司也针对自己的具体场景研发了自己的存储方案(如taobao开源的,facebook自主研发的),还有一些公司在现有开源项目(如hbase,fastdfs,mfs等)基础上做针对性改造优化以满足业务存储需求;
一.  通过对若干分布式存储系统的调研、测试与使用,与其它分布式系统相比,海量小文件存储更侧重于解决两个问题:
  1.&海量小文件的元数据信息组织与管理: 对于百亿量级的数据,每个文件元信息按100B计算,元信息总数据量为1TB,远超过目前单机服务器内存大小;若使用本地持久化设备存储,须高效满足每次文件存取请求的元数据查询寻址(对于上层有cdn的业务场景,可能不存在明显的数据热点),为了避免单点,还要有备用元数据节点;同时,单组元数据服务器也成为整个集群规模扩展的瓶颈;或者使用独立的存储集群存储管理元数据信息,当数据存储节点的状态发生变更时,应该及时通知相应元数据信息进行变更;
  对此问题,tfs/fastdfs设计时,就在文件名中包含了部分元数据信息,减小了元数据规模,元数据节点只负责管理粒度更大的分片结构信息;商用分布式文件系统龙存,通过升级优化硬件,使用分布式元数据架构&&多组(每组2台)高性能ssd服务器&&存储集群的元数据信息,满足单次io元数据查询的同时,也实现了元数据存储的扩展性;Haystack Directory模块提供了图片逻辑卷到物理卷轴的映射存储与查询功能,使用Replicated Database存储,并通过cache集群来降低延时提高并发,其对外提供的读qps在百万量级;
  2. 本地磁盘文件的存储与管理(本地存储引擎):对于常见的linux文件系统,读取一个文件通常需要三次磁盘IO(读取目录元数据到内存,把文件的inode节点装载到内存,最后读取实际的文件内容);按目前主流2TB~4TB的sata盘,可存储2kw~4kw个100KB大小的文件,由于文件数太多,无法将所有目录及文件的inode信息缓存到内存,很难实现每个图片读取只需要一次磁盘IO的理想状态,而长尾现象使得热点缓存无明显效果;当请求寻址到具体的一块磁盘,如何减少文件存取的io次数,高效地响应请求(尤其是读)已成为需要解决的另一问题;
  对此问题,有些系统(如tfs,Haystack)采用了小文件合并存储+索引文件的优化方案,此方案有若干益处:a.合并后的合并大文件通常在64MB,甚至更大,单盘所存存储的合并大文件数量远小于原小文件的数量,其inode等信息可以全部被cache到内存,减少了一次不必要的磁盘IO;b.索引文件通常数据量(通常只存储小文件所在的合并文件,及offset和size等关键信息)很小,可以全部加载到内存中,读取时先访问内存索引数据,再根据合并文件、offset和size访问实际文件数据,实现了一次磁盘IO的目的;c.单个小文件独立存储时,文件系统存储了其guid、属主、大小、创建日期、访问日期、访问权限及其它结构信息,有些信息可能不是业务所必需的,在合并存储时,可根据实际需要对文件元数据信息裁剪后在做合并,减少空间占用。除了合并方法外,还可以使用性能更好的SSD等设备,来实现高效响应本地io请求的目标。
  当然,在合并存储优化方案中,删除或修改文件操作可能无法立即回收存储空间,对于存在大量删除修改的业务场景,需要再做相应的考量。
二.  是近年越来越被广泛使用的分布式存储系统,其重要的创新之处是基于算法的计算寻址,真正的分布式架构、无中心查询节点,理论上无扩展上限(更详细ceph介绍见网上相关文章);Ceph的基础组件RADOS本身是对象存储系统,将其用于海量小文件存储时,CRUSH算法直接解决了上面提到的第一个问题;不过Ceph&OSD目前的存储引擎(,)对于上面描述的海量小文件第二个问题尚不能很好地解决;ceph社区曾对此问题做过描述并提出了基于rgw的一种(实际上,在实现本文所述方案过程中,发现了社区上的方案),不过在最新代码中,一直未能找到方案的实现;
  我们在Filestore存储引擎基础上对小文件存储设计了优化方案并进行实现,方案主要思路如下:将若干小文件合并存储在RADOS系统的一个对象(object)中,&小文件的名字、小文件在对象中的offset及小文件size&组成kv对,作为相应对象的扩展属性(或者omap,本文以扩展属性表述,ceph都使用kv数据库实现,如leveldb)进行存储,如下图所示,对象的扩展属性数据与对象数据存储在同一块盘上;
  使用本结构存储后,write小文件file_a操作分解为: 1)对某个object调用append小文件file_a;2)将小文件file_a在相应object的offset和size,及小文件名字file_a作为object的扩展属性存储kv数据库。read小文件file_a操作分解为:1)读取相应object的file_a对应的扩展属性值(及offset,size);2)读取object的offset偏移开始的size长度的数据。对于删除操作,直接将相应object的file_a对应的扩展属性键值删除即可,file_a所占用的存储空间延迟回收,回收以后讨论。另外,Ceph本身是强一致存储系统,其内在机制可以保证object及其扩展属性数据的可靠一致;
  由于对象的扩展属性数据与对象数据存储在同一块盘上,小文件的读写操作全部在本机本OSD进程内完成,避免了网络交互机制潜在的问题。另一方面,对于写操作,一次小文件写操作对应两次本地磁盘随机io(逻辑层面),且不能更少,某些kv数据库(如leveldb)还存在write amplification问题,对于写压力大的业务场景,此方案不能很好地满足;不过对于读操作,我们可以通过配置参数,尽量将kv数据保留在内存中,实现读取操作一次磁盘io的预期目标;
  如何选择若干小文件进行合并,及合并存储到哪个对象中呢?最简单地方案是通过计算小文件key的hash值,将具有相同hash值的小文件合并存储到id为对应hash值的object中,这样每次存取时,先根据key计算出hash值,再对id为hash值的object进行相应的操作;关于hash函数的选择,(1)可使用最简单的hash取模,这种方法需要事先确定模数,即当前业务合并操作使用的object个数,且确定后不能改变,在业务数据增长过程中,小文件被平均分散到各个object中,写压力被均匀分散到所有object(即所有物理磁盘,假设object均匀分布)上;object文件大小在一直增长,但不能无限增长,上限与单块磁盘容量及存储的object数量有关,所以在部署前,应规划好集群的容量和hash模数。(2)对于某些带目录层次信息的数据,如/a/b/c/d/efghi.jpg,可以将文件的目录信息作为相应object的id,及/a/b/c/d,这样一个子目录下的所有文件存储在了一个object中,可以通过rados的listxattr命令查看一个目录下的所有文件,方便运维使用;另外,随着业务数据的增加,可以动态增加object数量,并将之前的object设为只读状态(方便以后的其它处理操作),来避免object的无限增长;此方法需要根据业务写操作量及集群磁盘数来合理规划当前可写的object数量,在满足写压力的前提下将object大小控制在一定范围内。
  本方案是为小文件(1MB及以下)设计的,对于稍大的文件存储(几十MB甚至更大),如何使用本方案存储呢?我们将大文件large_file_a切片分成若干大小一样(如2MB,可配置,最后一块大小可能不足2MB)的若干小块文件:large_file_a_0,&large_file_a_1 ...&large_file_a_N,并将每个小块文件作为一个独立的小文件使用上述方案存储,分片信息(如总片数,当前第几片,大文件大小,时间等)附加在每个分片数据开头一并进行存储,以便在读取时进行解析并根据操作类型做相应操作。
  根据业务的需求,我们直接基于librados接口进行封装,提供如下操作接口供业务使用(c++描述):
int WriteFullObj(const std::string& oid, bufferlist& bl, int create_time = GetCurrentTime());
int Write(const std::string& oid, bufferlist& bl, uint64_t off, int create_time = GetCurrentTime());
int WriteFinish(const std::string& oid, uint64_t total_size, int create_time = GetCurrentTime());
int Read(const std::string& oid, bufferlist& bl, size_t len, uint64_t off);
int ReadFullObj(const std::string& oid, bufferlist& bl, int* create_time = NULL);
int Stat(const std::string& oid, uint64_t *psize, time_t *pmtime, MetaInfo* meta = NULL);
int Remove(const std::string& oid);
int BatchWriteFullObj(const String2BufferlistHMap& oid2data, int create_time = GetCurrentTime());
  对于写小文件可直接使用WriteFullObj;对于写大文件可使用带offset的Write,写完所有数据后,调用WriteFinish;对于读取整个文件可直接使用ReadFullObj;对于随机读取部分文件可使用带offset的Read;Stat用于查看文件状态信息;Remove用于删除文件;当使用第二种hash规则时,可使用BatchWriteFullObj提高写操作的吞吐量。
个人原创,转载请注明出处。
更多相关文章
8月27日下午,在IT168系统架构师大会存储与系统架构分论坛上,淘宝网技术委员会主席,淘宝网核心工程师章文嵩向我们详细介绍了淘宝网图片处理与存储系统的架构.章文嵩博士的演讲日程包括了淘宝的整个系统架构.淘宝图片存储系统架构,淘宝网独立开发的TFS集群文件系统,前端CDN系统以及淘宝网在节能服务器方 ...
####################################################################### 数据存储的趋势和大数据带来的挑战 分布式存储与CAP定理 分布式存储文件系统 Mogilefs基本原理 Mogilefs实现 Nginx反向代理Tracke ...
单台服务器作为Namenode,当文件数量规模不断增大时,元数据的规模增长将是一个需要面对的问题,由于Namenode需要将所有元数据Load到内存中,单台Namenode可能会无法管理海量的元数据.另一个是HDFS中SequenceFile存储方式的讨论,利用Block压缩方式可以很好的解决空间压 ...
转自:/2009/04/deal-with-tons-of-small-files/
Web2.0网站,数据内容以几何级数增长,尤其是那些小文件,几K-几百K不等,数量巨多,传统的文件系统处理起来很是吃力,很多网站在scaling的过程中都遇到了这 ...
一.简介 1.介绍 MogileFS 是一个开源的分布式文件系统,用于组建分布式文件集群,由 LiveJournal 旗下 Danga Interactive 公司开发,Danga 团队开发了包括 Memcached.MogileFS.Perlbal 等不错的开源项目:(注:Perlbal 是一个强 ...
class dbsys(models.Model):
'''用户-素材关系表'''
fid = models.CharField(max_length = 128)
username = models.CharField(default =u'ddd', max_length ...
http://code.taobao.org/trac/tfs/wiki/ZhWikiStart
海量小文件问题综述 海量小文件LOSF问题是工业界和学术界公认的难题,分析了LOSF问题的由来以及典型的应用场景,并简要阐述了当前文件系统在LOSF优化方面的进展.重点分析LOSF问题的根本原因,并给出具体的优化方法和策略,期望对LOSF问题的研究和优化实践提供一定的理论指导. 1.LOSF问题概述 ...
经常在项目中会遇到客户要求页面只显示自定义的内容, 不显示任何母版页中的元素; 说白了就是不套用任何母版页. 但是Sharepoint应用程序页又必须套用一个母版页, 那我们怎么隐藏呢? 我们可以使用css来隐藏母版 ...
首先来一个例子做实验 function createD(){ var dir = { name: &d&, ctrl: myCtrl, ctrl2: yourCtrl }; ...
1.什么是Chromium
Chromium 是Google公司的开源项目
1.http://sourceforge.net/projects/jython/下载jython包,把其中的jython.jar添加到工程目录
示例: 1.摘自:http://blog.csdn.net/anb ...
为什么要org 07c00h? 有很多人看了&自己动手写操作系统&或其第二版这本书后对这一行提出疑问. 这个问题在书中作者的解释是: 告诉编译器,将来我们的这段程序要被加载到07c00处执行.
Unreal发展史 引子四年前的一个深夜,或者说是一个早晨,Unreal的传奇开始了.它发生在马里兰州一个不起眼的市镇Rockvill,在一套公寓大楼里回响起一支墨西哥流浪乐队的曲子,那里住着Epic MegaGam ...
扩展功能的时候,尽量增加方法,而不要给方法增加参数.在总入口处可以选择增加参数. 数据是有形的.它在各种地方出现,它以各种形式出现.它像水一样,有固态,液态和气态. 这个世界的基因是用数据表达的---DNA和六十四卦 ...
linux调度算法在2.6.32中采用调度类实现模块式的调度方式.这样,能够很好的加入新的调度算法. linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法.这种模块化结构被称 ...
转自http://blog.chinaunix.net/uid--id-1828641.html 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代 ...
把一个double类型的值转成string,网上有很多的方法,包括用String.valueOf(Double d),或者 String str = && + d, 或者用DecimalForma ...

我要回帖

更多关于 分布式存储ceph 的文章

 

随机推荐