还不知道HBase冷热分离的技术原理?看这一篇就够了!
Posted 阿里云数据库
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了还不知道HBase冷热分离的技术原理?看这一篇就够了!相关的知识,希望对你有一定的参考价值。
-
优点: 方案简单,现成内核版本都能搞 -
缺点: 维护开销大,冷集群CPU存在浪费
-
优点: 同一集群冷热分离,维护开销少,更灵活的配置不同业务表的策略 -
缺点: 磁盘配比是个很大的问题,不同业务冷热配比是不一样的,比较难整合在一起,一旦业务变动,集群硬件配置是没法跟着变的。
1)增加冷表数据标记
2)根据标记增加写OSS的IO路径。
1.OSS并非文件系统
OSS并不是一个真正意义上的文件系统,它仅仅是两级映射 bucket/object,所以它是对象存储。你在OSS上看到类似这样一个文件:/root/user/gzh/file
。你会以为有3层目录+1个文件。实际上只有一个对象,这个对象的key包含了/
字符罢了。
这么带来的一个问题是,你要想在其上模拟出文件系统,你必须先能创建目录。很自然想到的是用/
结尾的特殊对象代表目录对象。Hadoop社区开源的OssFileSystem就是这么搞的。有了这个方法,就能判断到底存不存在某个目录,能不能创建文件,不然会凭空创建出一个文件,而这个文件没有父目录。
当然你这么做依然会有问题。除了开销比较大(创建深层目录多次HTTP请求OSS),最严重的是正确性的问题。
试想一下下面这个场景:
把目录/root/user/source
rename 成 /root/user/target
。这个过程除了该目录,它底下的子目录,子目录里的子文件都会跟着变。
类似这样:/root/user/source/file => /root/user/target/file
。这很好理解,文件系统就是一颗树,你rename目录,实际是把某颗子树移动到另一个节点下。这个在NameNode里的实现也很简单,改变下树结构即可。
但是如果是在OSS上,你要做rename,那你不得不递归遍历/root/user/source
把其下所有目录对象,文件对象都rename。因为你没法通过移动子树这样一个简单操作一步到位。
这里带来的问题就是,假设你递归遍历到一半,挂了。那么就可能会出现一半目录或文件到了目标位置,一半没过去。这样rename这个操作就不是原子的了,本来你要么rename成功,整个目录下的内容到新的地方,要么没成功就在原地。
所以正确性会存在问题,像HBase这样依赖rename操作将临时数据目录移动到正式目录来做数据commit,就会面临风险。
2.OSS rename实则是数据拷贝
前面我们提到了rename,在正常文件系统中应该是一个轻量级的,数据结构修改操作。但是OSS并没有rename这个操作实际上,rename得通过 CopyObject
+ DeleteObject
两个操作完成。首先是copy成目标名字,然后delete掉原先的Object。
这里有2个明显的问题,一个是copy是深度拷贝开销很大,直接会影响HBase的性能。另一个是rename拆分成2个操作,这2个操作是没法在一个事物里的,也就是说:可能存在copy成功,没delete掉的情况,此时你需要回滚,你需要delete掉copy出来的对象,但是delete依然可能不成功。所以rename操作本身实现上,正确性就难以保证了。
解决核心难点A
ApsaraDistributedFileSystem
类(以下简称ADFS)负责管理这两类存储文件系统,并且由ADFS负责感知冷热文件。
-
ApsaraDistributedFileSystem: 总入口,负责管理冷存和主存,数据该从哪里读,该写入哪里(ADFS)。 主存:PrimaryStorageFileSystem默认实现是
DistributedFileSystem(HDFS) -
冷存: ColdStorageFileSystem 默认实现是 HBaseOssFileSystem(HOFS) ,基于OSS实现的Hadoop API文件系统,可以模拟目录对象单独使用,也可以只作为冷存读写数据,相比社区版本有针对性优化,后面会讲。
核心难点B
-
主存索引文件存在,冷存数据文件不存在 -
冷存数据文件存在,主存索引文件不存住 -
主存索引文件信息不完整,无法定位冷存数据文件
冷热文件标记
对于主存,需要实现给文件冷热标记的功能,通过标记判断要打开怎样的数据读写流。这点NameNode可以通过给文件设置StoragePolicy实现。这个过程就很简单了,不详细赘述,下面说HBaseOssFileSystem写入优化设计。
HBaseOssFileSystem 写入优化
在说HOFS写设计之前,我们先要理解Hadoop社区版本的OssFileSystem设计(这也是社区用户能直接使用的版本)。
社区版本写入设计
Write -> OutputStream -> disk buffer(128M) -> FileInputStream -> OSS
这个过程就是先写入磁盘,磁盘满128M后,将这128M的block包装成FileInputStream再提交给OSS。这个设计主要是考虑了OSS请求成本,OSS每次请求都是要收费的,但是内网流量不计费。如果你1KB写一次,费用就很高了,所以必须大块写。而且OSS大文件写入,设计最多让你提交10000个block(OSS中叫MultipartUpload),如果block太小,那么你能支持的最大文件大小也是受限。
所以要攒大buffer,另外一个因素是Hadoop FS API提供的是OutputStream让你不断write。OSS提供的是InputStream,让你提供你要写入内容,它自己不断读取。这样你必然要通过一个buffer去转换。
这里会有比较大的一个问题,就是性能慢。写入磁盘,再读取磁盘,多了这么两轮会比较慢,虽然有PageCache存在,读取过程不一定有IO。那你肯定想,用内存当buffer不就好了。
内存当buffer的问题就是前面说的,有费用,所以buffer不能太小。所以你每个文件要开128M内存,是不可能的。更何况当你提交给OSS的时候,你要保证能继续写入新数据,你得有2块128M内存滚动,这个开销几乎不能接受。
HBaseOssFileSystem 写入设计
我们既要解决费用问题,也要解决性能问题,同时要保证开销很低,看似不可能,那么怎么做呢?
这里要利用的就是这个InputStream,OSS让你提供InputStream,并从中读取你要写入的内容。那么我们可以设计一个流式写入,当我传入这个InputStream给OSS的时候,流中并不一定得有数据。此时OSS调read读取数据会block在read调用上。等用户真的写入数据,InputStream中才会有数据,这时候OSS就能顺利读到数据。
当OSS读了超过128M数据时候,InputStream会自动截断,返回EOF,这样OSS会以为流已经结束了,那么这块数据就算提交完成。
所以我们本质只要开发这么一个特殊的InputStream即可。用户向Hadoop API提供的OutputStream中写入数据,数据每填满一个page(2M)就发给InputStream让其可读取。OuputStream相当于生产者,InputStream相当于消费者。
这里的内存开销会非常低,因为生产者速度和消费者速度相近时,也就2个page的开销。最后将这整套实现封装成OSSOutputStream类,当用户要写入冷文件时,实际提供的是OSSOutputStream,这里面就包含了这个特殊InputStream的控制过程。
当然实际生产中,我们会对page进行控制,每个文件设置最多4个page。并且这4个page循环利用,减少对GC对影响。所以最后我们得到下面一个环形缓冲的写入模式:
性能对比1:社区版本 vs 云HBase版
因为不用写磁盘,所以写入吞吐可以比社区的高很多,下图为HBase1.0上测试结果。在一些大KV,写入压力更大的场景,实测可以接近1倍。这个比较是通过替换ADFS冷存的实现(用社区版本和云HBase版本),都避免了rename深拷贝问题。如果直接裸用社区版本而不用ADFS那性能会差数倍。
性能对比2:热表 vs 冷表
云数据库HBase降价狂欢
首购爆款6个月仅需9.9元
首购入门款全年仅1元
新购一年低至5折
点击阅读原文
了解更多优惠
阿里巴巴数据库技术
微信:alibabadba
分享数据库前沿
解构实战干货
长按二维码关注
以上是关于还不知道HBase冷热分离的技术原理?看这一篇就够了!的主要内容,如果未能解决你的问题,请参考以下文章
还不懂什么是Redis?一文详解Redis,入门学习看这一篇就够了