HBase存储小文件(小于10K)十分出色,读写延迟低。对文档、图片和其他中等大小文件(100K-10M)的存储需求日益增长,并且要保持读写低延迟。一个典型的场景就是,银行存储客户的签字或扫描的文档。另一个典型的场景,交通部门保存路况或过车快照。通常只写入一次的中等大小文件。
存储这类文件时,由于 compaction 会导致性能下降。一个可能的场景,交通摄像头每天产生1TB的照片存到 Hbase 里,不断的 flush 生成一些小文件。根据 compaction 策略,数据可能会经过多次 compaction,数据因为压缩被重复写入新的大文件中。随着中等大小文件(moderate objects, MOBs)的积累,compaction 产生的读写会使 compaction 变慢,进一步阻塞 Memstore flush,最终阻塞更新。大量的 MOB 存储会触发频繁的 region split,相应region的可用性下降。
为了解决这个问题,Hbase的实现了对MOB的支持 (hbase-11339: HBase MOB,被合入的2.0.0版本,可以在CDH 5.4.x中获取)。
对 MOB 的操作通常集中在写入,很少更新或删除,读取不频繁。MOB 通常跟元数据一起被存储。元数据相对 MOB 很小,通常用来统计分析,而 MOB 一般通过明确的 row key 来获取。
用户希望在 HBase 中用相同的 API 来读写 MOB 文件,并且保持低延迟,强一致性、安全、快照和 HBase 副本等特性。要达到这一目标,必须将 MOB 从 HBase 主要的读写目录移到新的读写目录。
可行方案分析
第一种方案,选择优化 split 和 compaction 策略,设置一个更大的 MaxFileSize 来降低 region split 频率,减少或者不压缩来避免写入放大。这样会改善写入延迟,吞吐量好得多。但是,随着 store file 数量的增长,单个 store 会打开非常多的 reader,甚至超过操作系统的限制。结果就是内存被耗光,性能下降。
less compaction另外一种方式,可以采用 HBase+HDFS 的方式来分开存储元数据和 MOB 文件。HBase 存储一个文件的连接。这是客户端的解决方案,并且事务在客户端控制。MOB 不会消耗 HBase 的内存。存储的对象可以超过 50MB,但是大量的小文件使HDFS利用率不高,因为默认的块大小是128M。
hdfs举个例子,Namenode 有48G内存,每个文件100KB,3个副本。每个文件在 Namenode 内存中占用300字节,48G内存可以存大约1.6亿文件,限制了存储的总文件大小仅仅16T。
作为改进,可以将许多小的 MOB 合成一个大文件,一个文件有多个 MOB 入口,通过存储偏移量(offset)和长度来加快读取。不过维护数据一致性,管理删除的文件和压缩后的小文件十分困难。而且,我们还需要考虑安全策略,失去写数据的原子性,可能会丢失由复制和快照提供的备份和灾难恢复。
imageHBase MOB 架构设计
MOB由于大部分担心来自于压缩带来的IO,将 MOB 移出普通 region 的管理来避免 region split 和 compaction。HBase MOB 设计类似于 HBase+HDFS 的方式,将元数据和 MOB 分开存放。
不同的是服务端的设计:MOBs 在被刷到磁盘前缓存在 memstore 里,每次刷新 MOBs 被写入 MOB File(特殊的 HFile)。每个 MOB File 有多个入口相比于每个 MOB 一个 HDFS 文件的方式。MOB File 被放在特殊的 region 管理。读写都通过现有的Hbase API。
MOB文件读写
MOB 有一个最小值(阈值):如果 cell 长度大于这个值,这个 cell 就被认为是一个 MOB cell。
image当 MOB cell 被更新时,会被写入 WAL 和 Memstore,跟正常的 cell 没区别。当刷新的时候,MOBs 被刷新到 MOB file,元数据和 MOB file 的路径被刷入 store file。数据一致性和副本都是原生的。
通过改变阈值,cell 可以在 store file 和压缩过的 MOB file 之间移动,默认的阈值设置为100KB。
如下图,Store file 中包含 MOB file 路径的 cell 被叫做 reference cells。Tags 在单元格中保留,所以我们可以继续依赖 Hbase 的安全机制。
imagereference cells 通过 reference tags 与正常的 cells 区分。reference tag 表示 MOB file 中的一个 MOB cell,因此需要在读取的时候进一步转换。
image读取的时候,store scanner 会扫描 memstore 和 store file,如果遇到 reference cell,scanner 会读取单元格里的文件路径,通过相同的 row key 到 MOB file 中查找。扫描 MOB file 可以启用 block cache,可以加速查找。
这里不需要打开所有 MOB files 的 reader,只需要打开一个。随机读取不会受文件数量的影响,所以,不需要一遍又一遍的压缩足够大的文件。
MOB 文件名是可读的。由3部分组成,start key 的MD5值 + MOB 文件中的 cells 最新日期 + UUID。通常,MOB 有一个用户定义的过期时间,因此可以通过比较第二部分来找到、删除过期的 MOB files。
快照(snapshot)
为了更友好地使用快照特性,这些 MOB file 存储在一个特殊的虚拟 region 中。因此,快照、表导出/复制和存档会按预期的方式工作。
当生成 table 快照时,会在快照中生成 MOB region,并将现有的 MOB file 添加到 manifast 中。还原快照时, 在 MOB region 中创建文件链接。
清理和压缩
这两种情况需要删除MOB文档:1. 文件过期(设置TTL) 2. 文件太小,被合并到大文件里来改善 HDFS 利用率,合并后会删除。
HMaster 有一个工作线程(chore),扫描 MOB files,找到过期的文件并且删除。
如果写入的行只有少数的 cell 是 MOBs,MOB file 可能相对较小(与 HDFS block 相比)。并且,可能还有被删除的 cells。你需要清理删掉的 cells,并将小文件合并成大文件提高 HDFS 利用率。HBase 只压缩小文件,不涉及大文件,避免了对大文件重复压缩。
需要记住下面几条:
1. Hbase的 major compaction,cell 被删除之前,会先写入 del file。
2. 在 MOB compaction 的第一步,del file 会合并成更大的文件。
3. 如果 MOB 文件都是小文件,这种压缩被认为是一个 major compaction,被称为 ALL_FILES 压缩。
4. 这些选定的文件由文件名中的 startKey 和 date 进行分组处理(partition)。每个分区中的小文件和 del 文件一起压缩,这样删除的单元格就会被丢弃。同时一个新的 HFile 与新的 reference cells 产生,压缩器提交新的 MOB file,然后 bulk load 这个 HFile 到 HBase。
下面说明了MOB文件的生命周期。它们是在 memstore flush 时创建的,当它们未被快照引用(referenced by snapshot)或在存档中过期(expired in archive)时,由 HFileCleaner 从文件系统中删除。
image总之,新的 HBase MOB 设计将中等大小文件移出 HBase 的主要读写路径,同时保留大多数安全性、压缩性和快照特性。它迎合了 MOB 操作的特点,使大量的 MOBs 写入更可预测,并保持读写低延迟。
参考:https://blog.cloudera.com/blog/2015/06/inside-apache-hbases-new-support-for-mobs/
网友评论