HBase最佳实践-列族设计优化 – 有态度的HBase/Spark/BigData

标签: | 发表时间:2020-04-22 18:15 | 作者:
出处:http://hbasefly.com

随着大数据的越来越普及,HBase也变得越来越流行。会用HBase现在已经变的并不困难,然而,怎么把它用的更好却并不简单。那怎么定义‘用的好’呢?很简单,在保证系统稳定性、可用性的基础上能够用最少的系统资源(CPU,IO等)获得最好的性能(吞吐量,读写延迟)就是’用的好’。HBase是一个庞大的体系,涉及到很多方面,很多因素都会影响到系统性能和系统资源使用率,根据场景对这些配置进行优化会很大程度上提升系统的性能。笔者总结至少有如下几个方面: HDFS相关配置优化,HBase服务器端优化(GC优化、Compaction优化、硬件配置优化),列族设计优化,客户端优化等,其中客户端优化在前面已经通过超时机制、重试机制讲过,后面笔者会继续分别介绍其他三个优化重点。

本节重点介绍列族设计优化,HBase中基本属性都是以列族为单位进行设置的,如下示例,用户创建了一张称为‘ NewsClickFeedback’的表,表中只有一个列族’Toutiao’,紧接着的属性都是对此列族进行的设置。这些属性基本都会或多或少地影响该表的读写性能,但有些属性用户只需要理解其意义就知道如何设置,而有些属性却需要根据场景、根据业务来设置,比如BLOCKSIZE属性在不同场景下应该如何设置?还有COMPRESSION属性和DATA_BLOCK_ENCODING属性,两者都可以提供压缩功能,那到底应该选择哪个,还是两个都需要进行设置?本文就重点介绍这三个属性的设计原则。

create 'NewsClickFeedback',{NAME=>'Toutiao',VERSIONS=>1,BLOCKCACHE=>true,BLOOMFILTER=>'ROW',COMPRESSION=>'SNAPPY',TTL => '259200', DATA_BLOCK_ENCODING => 'PREFIX_TREE', BLOCKSIZE => '65536'},{SPLITS => ['1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']}

BlockSize设置

块大小是HBase的一个重要配置选项,默认块大小为64K。对于不同的业务数据,块大小的合理设置对读写性能有很大的影响。而对块大小的调整,主要取决于两点:

1. 用户平均读取数据的大小。理论上讲,如果用户平均读取数据的大小较小,建议将块大小设置较小,这样可以使得内存可以缓存更多block,读性能自然会更好。相反,建议将块大小设置较大。

为了更好说明上述原理,笔者使用YCSB做了一个测试,分别在Get、Scan两种场景下测试不同BlockSize大小(16K,64K,128K)对性能的影响。测试结果分别如下面两图:

clipboard2


随着BlockSize的增大,系统随机读的吞吐量不断降低,延迟不断增大。64K大小比16K大小的吞吐量大约降低13%,延迟增大13%。同样的,128K大小比64K大小的吞吐量降低约22%,延迟增大27%。因此,对于以随机读为主的业务,可以适当调低BlockSize的大小,以获得更好的读性能。

clipboard

随着BlockSize增大,scan的吞吐量逐渐增大,延迟不断降低。64K大小BlockSize比16K大小的吞吐量增加了33%,延迟降低了24%;128K大小比64K大小吞吐量增加了7%,延迟降低了7%;因此,对于以scan为主的业务,可以适当增大BlockSize的大小,以获得更好的读性能。

可见,如果业务请求以Get请求为主,可以考虑将块大小设置较小;如果以Scan请求为主,可以将块大小调大;默认的64K块大小是在Scan和Get之间取得的一个平衡。

2. 数据平均键值对规模。可以使用HFile命令查看平均键值对规模,如下:

./hbase org.apache.hadoop.hbase.io.hfile.HFile -m -f /hbase-sc/data/news/NewsClickFeedback/627b1d95153d4157351b65135ab701a3/Toutiao/011b41375e584530a24a3a203b9ce1a3
Block index size as per heapsize: 704
reader=/hbase-sc/data/news/NewsClickFeedback/627b1d95153d4157351b65135ab701a3/Toutiao/011b41375e584530a24a3a203b9ce1a3,
    compression=snappy,
    cacheConf=CacheConfig:disabled,
    firstKey=a0000000be3d27a5a11d203f798781a9/Toutiao:ClickViewTS/1465783628056/Put,
    lastKey=a80072cbf409d04542f272446d4b65a4/Toutiao:ClickViewTS/1465776679416/Put,
    avgKeyLen=62,
    avgValueLen=93,
    entries=6451829,
    length=698454846
......

从上面输出的信息可以看出,该HFile的平均键值对规模为62B + 93B = 155B,相对较小,在这种情况下可以适当将块大小调小(例如32KB)。这样可以使得一个block内不会有太多kv,kv太多会增大块内寻址的延迟时间,因为HBase在读数据时,一个block内部的查找是顺序查找。

注意: 默认块大小适用于多种数据使用模式,调整块大小是比较高级的操作。配置错误将对性能产生负面影响。因此建议在调整之后进行测试,根据测试结果决定是否可以线上使用。

数据编码/压缩

Compress/DeCompress

数据压缩是HBase提供的另一个特性,HBase在写入数据块到HDFS之前会首先对数据块进行压缩,再落盘,从而可以减少磁盘空间使用量。而在读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache,最后返回给用户。写路径和读路径分别如下:

3

结合上图,来看看数据压缩对资源使用情况以及读写性能的影响:

(1) 资源使用情况:压缩最直接、最重要的作用即是减少数据硬盘容量,理论上snappy压缩率可以达到5:1,但是根据测试数据不同,压缩率可能并没有理论上理想;压缩/解压缩无疑需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块会先被解压,缓存到内存中的block是解压后的,因此和不压缩情况相比,内存前后基本没有任何影响。

(2) 读写性能:因为数据写入是先将kv数据值写到缓存,最后再统一flush的硬盘,而压缩是在flush这个阶段执行的,因此会影响flush的操作,对写性能本身并不会有太大影响;而数据读取如果是从HDFS中读取的话,首先需要解压缩,因此理论上读性能会有所下降;如果数据是从缓存中读取,因为缓存中的block块已经是解压后的,因此性能不会有任何影响;一般情况下大多数读都是热点读,缓存读占大部分比例,压缩并不会对读有太大影响。

可见,压缩特性就是使用CPU资源换取磁盘空间资源,对读写性能并不会有太大影响。HBase目前提供了三种常用的压缩方式:GZip | LZO | Snappy,下面表格是官方分别从压缩率,编解码速率三个方面对其进行对比:

5

综合来看,Snappy的压缩率最低,但是编解码速率最高,对CPU的消耗也最小,目前一般建议使用Snappy。

Encode/Decode

除了数据压缩之外,HBase还提供了数据编码功能。和压缩一样,数据在落盘之前首先会对KV数据进行编码;但又和压缩不同,数据块在缓存前并没有执行解码,因此即使后续命中缓存的查询也是编码的数据块,需要解码后才能获取到具体的KV数据。写路径和读路径分别如下:


4

同样,来看看数据编码对资源使用情况以及读写性能的影响:

(1) 资源使用情况:和压缩一样,编码最直接、最重要的作用也是减少数据硬盘容量,但是数据编码压缩率一般没有数据压缩的压缩率高,理论上只有5:2;编码/解码一般也需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块并没有被解码,缓存到内存中的block是编码后的,因此和不编码情况相比,相同数据block快占用内存更少,即内存利用率更高。

(2) 读写性能:和数据压缩相同,数据编码也是在数据flush到hdfs阶段执行的,因此并不会直接影响写入过程;前面讲到,数据块是以编码形式缓存到blockcache中的,因此同样大小的blockcache可以缓存更多的数据块,这有利于读性能。另一方面,用户从缓存中加载出来数据块之后并不能直接获取KV,而需要先解码,这却不利于读性能。可见,数据编码在内存充足的情况下会降低读性能,而在内存不足的情况下需要经过测试才能得出具体结论。

HBase目前提供了四种常用的编码方式:Prefix | Diff | Fast_Diff | Prefix_Tree。下图是Prefix_Tree编码算法作者做的一个测试结果:



可见,prefix_tree压缩算法在不同的block size下性能都比较稳定,而另外两种压缩算法的查找性能会随着blocksize直线下降。对于我们默认的64K的block大小,性能相差40+倍。另外,阿里天梧大牛之前在一篇博文里面做过测试证明了PREFIX_TREE算法的优越性,见《 HBase-0.96中新BlockEncoding算法-PREFIX_TREE压缩的初步探究及测试》,因此一般建议使用PREFIX_TREE编码压缩。

选择哪一个?Why?

综上上面分析,数据压缩和数据编码使命基本相同:消耗CPU资源压缩数据大小,可以认为是一种时间换空间的做法。但,同时开启两个功能会不会更好?如果只需要开启一个,优先选择哪一个?

为了更加深刻地认识数据压缩编码,回答上面两个问题,本人在测试环境使用YCSB做了一个简单的测试,分别在四种场景下(无压缩无编码、仅压缩、仅编码、既压缩既编码)对随机读以及扫描读的操作延时、CPU使用率以及对应的压缩率进行了测试。

测试条件:

数据:6000w条记录,一个列族,每个列族10个列,单条记录总共1K大小;

硬件:单RegionServer,3G BlockCache,CPU: 32  Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz

测试结果:







结果分析:

1. 数据压缩率并没有理论上0.2那么高,只有0.7左右,这和数据结构有关系。其中压缩、编码、压缩+编码三种方式的压缩率基本相当。

2. 随机读场景:和默认配置相比,snappy压缩在性能上没有提升,CPU开销却上升了38%;prefix_tree性能上没有提升,CPU利用率也基本相当;snappy+prefix_tree性能没有提升,CPU开销上升了38%。

3. 区间扫描场景:和默认配置相比,snappy压缩在性能上略有10%的提升,但是CPU开销却上升了23%;prefix_tree性能上略有4%左右的下降,但是CPU开销也下降了5%;  

snappy+prefix_tree在性能上基本没有提升,CPU开销却上升了23%;


注意:虽然从压缩率、CPU利用率以及性能上来看,prefix_tree编码确实会比snappy压缩更加优秀。但本人在线上遇到了很大的坑:compaction一直卡住,详见HBASE-12959,后来查看相关问题,发现还可能造成scan miss,详见HBASE-12817。后来发现该功能目前还属于实验性质特性-experimental feature。鉴于安全考虑,prefix_tree功能建议不要设置。


到此为止,本文主要介绍了HBase的一个优化方向:列族设计优化。其中重点介绍了BlockSize在不同场景下对读写性能的影响,以及Compress与Data_Block_Encoding的设计原则。希望看官能够根据上述对HBase的列族优化有一个更好的认识,并且能够更多地通过测试来巩固认知。需要说明的是,这里的设计原则对大多数应用业务都是有效的,也有可能对于某些特殊场景并不适用,因此对于比较敏感的业务,还是以实际测试为准。后期笔者将会继续分析HBase优化这个专题,接着介绍HBase体系中另一个非常非常重要的概念-Compaction,敬请期待~


 

相关 [hbase 最佳实践 设计] 推荐:

HBase最佳实践-列族设计优化 – 有态度的HBase/Spark/BigData

- -
随着大数据的越来越普及,HBase也变得越来越流行. 会用HBase现在已经变的并不困难,然而,怎么把它用的更好却并不简单. 很简单,在保证系统稳定性、可用性的基础上能够用最少的系统资源(CPU,IO等)获得最好的性能(吞吐量,读写延迟)就是’用的好’. HBase是一个庞大的体系,涉及到很多方面,很多因素都会影响到系统性能和系统资源使用率,根据场景对这些配置进行优化会很大程度上提升系统的性能.

HBase最佳实践-写性能优化策略 – 有态度的HBase/Spark/BigData

- -
上一篇文章主要介绍了HBase读性能优化的基本套路,本篇文章来说道说道如何诊断HBase写数据的异常问题以及优化写性能. 和读相比,HBase写数据流程倒是显得很简单:数据先顺序写入HLog,再写入对应的缓存Memstore,当Memstore中数据大小达到一定阈值(128M)之后,系统会异步将Memstore中数据flush到HDFS形成小文件.

HBase最佳实践-读性能优化策略 – 有态度的HBase/Spark/BigData

- -
任何系统都会有各种各样的问题,有些是系统本身设计问题,有些却是使用姿势问题. HBase也一样,在真实生产线上大家或多或少都会遇到很多问题,有些是HBase还需要完善的,有些是我们确实对它了解太少. 总结起来,大家遇到的主要问题无非是Full GC异常导致宕机问题、RIT问题、写吞吐量太低以及读延迟较大.

HBase最佳实践-用好你的操作系统

- - IT瘾-bigdata
终于又切回HBase模式了,之前一段时间因为工作的原因了解接触了一段时间大数据生态的很多其他组件(诸如Parquet、Carbondata、Hive、SparkSQL、TPC-DS/TPC-H等),虽然只是走马观花,但也受益良多. 对视野、思维模式都有极其重要的作用,至少,扩展了大数据领域的对话圈.

HBase表设计

- - 互联网 - ITeye博客
默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据, 直到这 个region足够大了才进行切分. 一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按 照 region分区情况,在集群内做数据的负载均衡.

HBase Schema 设计

- - IT瘾-dev
HBase 与传统关系数据库(例如MySQL,PostgreSQL,Oracle等)在架构的设计以及为应用程序提供的功能方面有很大的不同. HBase 权衡了其中一些功能,以实现更好的可扩展性以及更灵活的模式. 与关系数据库相比,HBase 表的设计有很大的不同. 下面将通过解释数据模型向您介绍 HBase 表设计的基础知识,并通过一个例子深入探讨 HBase 表的设计.

HBase RowKey 设计

- - IT瘾-dev
1.1 RowKey对查询的影响. HBase中 RowKey 用来唯一标识一行记录. 在 HBase 中检索数据有以下三种方式:. 通过 get 方式,指定 RowKey 获取唯一一条记录. 通过 scan 方式,设置 startRow 和 endRow 参数进行范围匹配. 全表扫描,即直接扫描整张表中所有行记录.

数据库设计的最佳实践

- - CSDN博客数据库推荐文章
1、使用定义明确的表或列名,并保持一致(例如,School、StudentCourse、CourseID). 2、使用单数形式的表名(即,用StudentCourse而非StudentCourses). 表代表了实体的合集,不需要复数形式. 否则你将在定义表时不得不使用“{”、“[”等字符(即为了访问表Student Course,你须得书写“Student Course”.

ActiveMQ架构设计与最佳实践

- - 深入一点,你会更加快乐
    ActiveMQ是最常用、特性最丰富的消息中间件,通常用于消息异步通信、调用解耦等多种场景,是JMS规范的实现者之一.     ActiveMQ提供两种可供实施的架构模型:“M-S”和“network bridge”;其中“M-S”是HA方案,“网络转发桥”用于实现“分布式队列”.     Master-Slave模型下,通常需要2+个ActiveMQ实例,任何时候只有一个实例为Master,向Client提供"生产"、“消费”服务,Slaves用于做backup或者等待Failover时角色接管.

RESTful API 设计最佳实践

- - 文章 – 伯乐在线
项目资源的URL应该如何设计. 用哪种HTTP方法来创建一个新的资源. 实现分页和版本控制的最好方法是什么. 因为有太多的疑问,设计RESTful API变得很棘手. 在这篇文章中,我们来看一下RESTful API设计,并给出一个最佳实践方案. 资源集合用一个URL,具体某个资源用一个URL:. #资源集合的URL /employees/56.