本帖最后由 非鱼 于 2015-4-29 12:23 编辑
方案1:HBase自身的大对象存储方案
由于HBase底层数据都是以Bytes数组来存储,对于非结构化数据的大对象可以很容易的转成Bytes数组存进HBase。另一方面,由于HBase是一个按列存储的数据库,在一个大表中,为了不让大对象影响其他结构化数据的读性能,可以将大对象数据单独存储进HBase表的单个Column Family中。
这种方案的优势在于:
优势一:实现简单。充分利用HBase自身特点,按列存储,将大对象数据单独存为一个列族。不需要任何额外的代码或功能的引入。
优势二:数据管理方便。将大对象数据的管理完全交给HBase自身的机制,和其他数据一样以StoreFile形式存储在Region中,并按照HBase对于Region的管理方式统一进行迁移、合并、删除等操作。
优势三:保证一致性。延续HBase本身的强一致性和管理方式,保证其大对象数据的一致性。
而这种方案的缺点也很明显:
缺陷一:无法回避Split和Compaction,写性能较差。之前己经提到过HBase由于大对象的影响,在写入时容易频繁的触发Split和Compaction,由于Split对 于写操作的阻塞和Compaction对于集群I/O的占用,将直接对写性能造成直接的不良影响。缓慢的Compaction操作也会导致Flush的延时,从而阻塞住客户端的更新。
缺陷二:不稳定的延时。由于Split和Compaction带来的影响,导致其Flush过程被延时从而引发MemStore的不断增长导致客户端的插入被锁住,这种延时一方面难以满足实时系统的低延时要求,另一方面是其不稳定的延时有可能会导致超时异常而引发不必要的重试。
方案2:基于HDFS的HBase大对象存储方案
由于大对象数据容量过大导致频繁的触发Split和Compaction从而引发客户端写的阻塞,所以很容易的能够想到,如果我们将大对象数据排除在HBase本身的写流程之外,就可以很大程度的减少频繁触发HBase Split和Compaction的因素的影响力,减少对于HBase的写性能影响。
由于HBase本身依赖于分布式文件系统HDFS,将大对象数据直接存储在HDFS上,而将结构化数据及大对象文件索引存储在HBase上面,仅仅让HBase管理容量较小的结构化数据和大对象的文件索引,其Split和Compaction的触发程度将大大减少,确实会提高HBase大对象存储的写性能。
而大对象存储在HDFS上的形式也主要分为两类:
方案A是直接将每条大对象数据以一个文件的形式存储到HDFS上,而HBase存储每个大对象的文件地址即可。
此方案的优点在于实现较为简单。客户端可以直接利用HDFS的API接口实现数据的put,让大对象数据以最简单的方式回避了 HBase的flush机制。但是其主要缺陷在于会产生大量的小文件,而大量小文件会在一定程度上影响HDFS的整体性能。
方案B是将多条大对象数据存储在一个Sequence File中。
由于A方案中将每条大对象数据作为一个文件写入HDFS会产生大量小文件从而造成对Namenode的巨大压力,所以业界很快幵始使用HDFS自己的文件格式SequenceFile。将多条大对象数据存储在一个Sequence File中而在HBase中存储其结构化数据和Sequence File的文件链接地址及其偏移量。
SequenceFile文件是Hadoop用来存储二进制形式的key-value对而设计的一种平面文件(Flat File)。目前,也有不少人在该文件的基础之上提出了一些
HDFS中小文件存储的解决方案,他们的基本思路就是将小文件进行合并成一个大文件,同时对这些小文件的位置信息构建索引。不过,这类解决方案还涉及到Hadoop的另一种文件格式--MapFile文件。SequenceRle文件并不保证其存储的key-value数据是按照key的某个顺序存储的。
该方案的优点在于:实现较为方便,直接使用HDFS的Sequence File的API接口;同时回避了 A方案中出现大量小文件的问题。这种方案的主要缺点在于其无法保证一致性。其主要原因在于当HBase成功写入其结构化数据和大对象数据所在SequenceFile文件链接地址后,Sequencenie本身由于某些外部因素的触发,导致其写失败,该SequenceFile没有成功生成,该大对象数据也就没有成功的写入,其数据一致性被破坏。
综合A、B两种方案,总结其将大对象数据直接写到HDFS方案的优缺点,
其优势在于:
优势一:实现较为简单。整体实现方案仅基于HBase机制和HDFS本身的文件格式及其API接口,对于有一点Hadoop及HBase经验的工程师都能完成。
优势二:让大对象数据回避了 HBase的Split和Compaction机制,确实可以提升其写性能。
但是其缺陷也很明显:
缺陷一:需要客户端编写额外的代码。客户端在原本的HBase插入程序的基础上需要引入HDFS的文件插入接口,获取其HDFS文件链接并写入HBase中,在编程方面并不是特别友好。
缺陷二:大对象数据管理的困难。对于已经不再被引用的大对象数据和过时的大对象数据,由于其存在于HDFS上,若无其他人工干预将没有办法清理这些超时文件,长此以往将严重影响整个集群的性能。
方案3:基于列族(ColumnFamily,CF)定制的HBase自身方案
由于大对象的影响,在写入时HBase将频繁Compaction从而占用过高的集群j/0,导致其写性能降低的,而把大对象数据绕过HBase直接写成HDFS文件格式不方便管理,那我们接下来可能考虑的方向是在HBase机制内怎样才能回避掉 Region 的 Split 和 Compaction 阶段。
而其中的一个方向就是对存储大对象数据的ColumnFamily定制其Compaction机制,让其在插入过程中不执行Compaction操作HBase有多个参数来控制其Compaction的触发,其中一个比较关键的参数如下:
hbase.hstore.compaction.min ,这个参数的作用是控制最小Minor Compaction的文件个数。当一个Region中StoreFile的数量超过这个值时会幵始检查是否需要Compaction,同时该参数也是指可以被Compaction的最小文件个数,如果选取的文件数目小于它,则不会做Compaction。
所以,若仅针对存储大对象数据的CloumnFamily设置该Compaction参数,将其值调大,如计算机的无限大值(Long.MAX_VALUE),那么该CloumnFamily在写入过程中将不会有机会触发Minor Compaction,从而集群不会被大对象数据引发的频繁Compaction影响I/O性能,从而能在一定程度上提高写入性能而又不影响HBase对大对象数据的管理。
而该方案对于存储结构化数据的Column Family并不干扰,其存储结构化数据的ColumnFamily依然按照HBase的Compaction机制进行其Minor Compation。
此方案的优势在于:
优势一:实现方便。仅需要针对存储大对象数据的ColumnFamily设置其compaction参数即可。
优势二:数据管理方便。仍然将大对象数据存储在HBase自己的数据格式中,其大对象数据的管理依然交由HBase自身机制完成,利于数据的管理维护。
优势三:回避了 Minor Compaction对于写入性能的影响。单独禁用存储大对象的Column Family,减少因大对象频繁触发Compaction对于集群丨/〇性能的影响,提高其写入性能。
而其缺陷在于:
缺陷一:未解决Split带来的影响。该方案仅仅将大对象数据的Compaction机制给省略掉了,但是另一个影响写入性能的因素Split并没有在该方案中被考
虑到,由于大对象频繁触发Split引起客户端写入阻塞的影响仍未解决。
缺陷二:大量大对象的StoreFiles影响读性能。由于为大对象数据定制了无compaction机制,所以HBase的Region中会存储着大量的大对象Storefiles,这导致HBase的scan (顺序读多条数据)操作和随机读操作极慢,并且大量的StoreFiles会增加HBase的Block indexes (块索引)在内存中的存储负担,影
响其插入性能。
|