52

深入探讨为什么HBase Scan性能低下

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUxOTU5Mjk2OA%3D%3D&%3Bmid=2247483879&%3Bidx=1&%3Bsn=bd15b6c672eaddd66ebb1c62bd30c02c&%3Butm_source=tuicool&%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

原文:https://blog.51cto.com/12445535/2359652

与写流程对比起来,HBase读数据是一个更加复杂的操作流程,这主要基于两个方面的原因:

其一是因为整个HBase存储引擎基于LSM-Tree实现,因此一次范围查询可能会涉及多个分片、多块缓存甚至多个数据存储文件;

其二是因为HBase中更新操作以及删除操作实现都很简单,更新操作并没有更新原有数据,而是使用时间戳属性实现了多版本。删除操作也并没有真正删除原有数据,只是插入了一条打上"deleted"标签的数据,而真正的数据删除发生在系统异步执行Major_Compact的时候。

很显然,这种实现套路大大简化了数据更新、删除流程,但是对于数据读取来说却意味着套上了层层枷锁,读取过程需要根据版本进行过滤,同时对已经标记删除的数据也要进行过滤。

Scan流程

1、一次scan请求,先在客户端缓存中找,如果找到了后,就直接返回给客户端。

2、如果没有找到再在 BlockCache、HFile以及memcache 中一行一行进行扫描,扫到100行之后,就返回给客户端。

3、客户端将这100行数据缓存到内存中并返回给一条给上层业务。

这里是每次都会调用100行数据,客户端拿到之后,再扫描100条数据,直到数 据被全部拿到。

上层业务不断一条一条获取扫描数据,在数据量大的情况下实际上HBase客户端会不断发送next请求到HBase服务器。有的朋友可能会问为什么scan需要设计为多次next请求的模式?个人认为这是基于多个层面的考虑:

1、HBase本身存储了海量数据,所以很多场景下一次scan请求的数据量都会比较大。如果不限制每次请求的数据集大小,很可能会导致系统带宽吃紧从而造成整个集群的不稳定。

2、如果不限制每次请求的数据集大小,很多情况下可能会造成客户端缓存OOM掉。

3、如果不限制每次请求的数据集大小,很可能服务器端扫描大量数据会花费大量时间,客户端和服务器端的连接就会timeout。

get的批处理操作是按照目标region进行分组,不同分组的get请求会并发执行读取。然而scan并没有这样实现。 

也就是说,scan不是并行操作。

所以从客户端角度来看整个扫描时间=客户端处理数据时间+服务器端扫描数据时间,这能不能优化?

根据上面的分析,scan API的效率很大程度上取决于扫描的数据量。通常建议OL TP业务中少量数据量扫描的scan可以使用scan API。大量数据的扫描使用scan API,扫描性能有时候并不能够得到有效保证。

引出问题

HBase作为列式存储,为什么它的scan性能这么低呢,列式存储不是更有利于scan操作么?Parquet格式也是列式,但它的scan这么优秀,他们的性能差异并不是因为数据组织方式造成的么?Kudu也是采用的类LSM数据结构,但是却能达到Parquet的扫描速度(Kudu是纯列式的),Kudu的一个列也会形成很多文件,但是好像并没影响它的性能。

小结

1、HBase不完全是列式存储,确切的说是列族式存储,HBase中可以定义一个列族,列族下可以有都个列,这些列的数据是存在一起的。而且通常情况下我们建议列族个数不大于2个,这样的话每个列族下面必然会有很多列。因此HBase并不是列式存储,更有点像行式存储。

2、HBase扫描本质上是一个一个的随即读,不能做到像HDFS(Parquet)这样的顺序扫描。试想,1000w数据一条一条get出来,性能必然不会很好。

那么问题就来了,HBase为什么不支持顺序扫描?

这是因为HBase支持更新操作以及多版本的概念,这个很重要。可以说如果支持更新操作以及多版本的话,扫描性能就不会太好。原理是这样的,我们知道HBase是一个类LSM数据结构,数据写入之后先写入内存,内存达到一定程度就会形成一个文件,因此HBase的一个列族会有很多文件存在。因为更新以及多版本的原因,一个数据就可能存在于多个文件,所以需要一个文件一个文件 查找才能定位出具体数据。

所以HBase架构本身个人认为并不适合做大规模scan,很大规模的scan建议还是用Parquet,可以把HBase定期导出到Parquet来scan。

3、Kudu性能并没有达到Parquet的扫描速度,可以说介于HBase和Parquet之间:

  • Kudu比HBase扫描性能好,是因为Kudu是纯列存,扫描不会出现跳跃读的情况,而HBase可能会跳跃seek。这是本质的区别。

  • 但Kudu扫描性能又没有Parquet好,就是因为Kudu是LSM结构,它扫描的时候还是会同时顺序扫描多个文件,并比较key值大小。而Parquet只需要顺序对一个block块中的数据进行扫描即可。这个是两者的区别。

所以说HBase相比Parquet,这两个方面都是scan的劣势。

参考链接:

H Base原理-数据读取流程解析

http://hbasefly.com/2016/12/21/hbase-getorscan/

HBase最佳实践 – Scan用法大观园 

http://hbasefly.com/2017/10/29/hbase-scan-3/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK