hbase,hbase是关系型还是非关系型

http://www.itjxue.com  2023-01-05 03:35  来源:未知  点击次数: 

HBASE-Procedure架构

Hbase Procedure是HBASE服务端负责事物处理的一个架构,写的非常棒。。。可以扒下来改一改自己用的那种。。。

实现事物,需要处理的问题很多,主要的三个如下

对于事物的异常处理,要实现回滚操作。

对于未知状态处理,需要实现幂等与重试,当然实现分布式事物还有很多别的方面,这里省略。

HBASE的Procedure就是依据以上背景实现。

Procedure代表上述的流程部分,多个流程的划分为如果要执行下一个流程,那么需要将这两个流程之间的状态存储下来,也是幂等执行的最小单位。

最主要的三个方法为

Procedure之间是有派生关系的,即一个Procedure会派生出一个子Procedure,父Procedure和子Procedure共同组成一个事物,最顶层的流程为Root Procedure

对于资源的锁定:

执行Procedure的线程池。

保存事物的执行过程,用来在事物出错的时候,依次回滚事务,是一个栈结构。

用来在执行Procedure前存储Procedure的信息,用以在程序死亡重启时恢复执行事物。

用于组织执行Procedure的优先级

对Procedure的执行线程逻辑实现

深入理解HBASE(4)HFile

1)HFile由DataBlock、Meta信息(Index、BloomFilter)、Info等信息组成。

2)整个DataBlock由一个或者多个KeyValue组成。

3)在文件内按照Key排序。

这里只介绍V2版本的,HFileV1的数据格式在0.92版本升级到V2版本。

1)文件分为三部分:Scanned block section,Non-scanned block section,以及Opening-time data section

Scanned block section:表示顺序扫描HFile时(包含所有需要被读取的数据)所有的数据块将会被读取,包括Leaf Index Block和Bloom Block;

Non-scanned block section:HFile顺序扫描的时候该部分数据不会被读取,主要包括Meta Block即BloomFilter和Intermediate Level Data Index Blocks两部分;

Load-on-open-section:这部分数据在HBase的region server启动时,需要加载到内存中。包括FileInfo、Bloom filter block、data block index和meta block index;

Trailer:这部分主要记录了HFile的基本信息、各个部分的偏移值和寻址信息。

Leaf index block具体存储了DataBlock的offset、length、以及firstkey的信息。

RootDataIndex 存储的是每个Leaf index block的offset、length、Leaf index Block记录的第一个key,以及截至到该Leaf Index Block记录的DataBlock的个数。

假定DataBlock的个数足够多,HFile文件又足够大的情况下,默认的128KB的长度的ROOTDataIndex仍然存在超过chunk大小的情况时,会分成更多的层次。这样最终的可能是ROOT INDEX – IntermediateLevel ROOT INDEX(可以是多层) —〉Leaf index block

在ROOT INDEX中会记录Mid Key所对应的信息,帮助在做File Split或者折半查询时快速定位中间Row的信息。

HFile V2的写操作流程:

1)Append KV到 Data Block。在每次Append之前,首先检查当前DataBlock的大小是否超过了默认的设置,如果不超出阈值,写入输出流。如果超出了阈值,则执行finishBlock(),按照Table-CF的设置,对DataBlock进行编码和压缩,然后写入HFile中。//以Block为单位进行编码和压缩,会有一些性能开销,可以参考 HBase实战系列1—压缩与编码技术

2)根据数据的规模,写入Leaf index block和Bloom block。

Leaf index Block,每次Flush一个DataBlock会在该Block上添加一条记录,并判断该Block的大小是否超过阈值(默认128KB),超出阈值的情况下,会在DataBlock之后写入一个Leaf index block。对应的控制类:HFileBlockIndex,内置了BlockIndexChunk、BlockIndexReader和BlockIndexWriter(实现了InlineBlockWriter接口)。

Bloom Block设置:默认使用MURMUR hash策略,每个Block的默认大小为128KB,每个BloomBlock可以接收的Key的个数通过如下的公式计算,接收的key的个数 与block的容量以及errorRate的之间存在一定的关系,如下的计算公式中,可以得到在系统默认的情况下,每个BloomBlock可以接纳109396个Key。

注意:影响BloomBlock个数的因素,显然受到HFile内KeyValue个数、errorRate、以及BlockSize大小的影响。可以根据应用的需求合理调整相关控制参数。

每一个BloomBlock会对应index信息,存储在Meta Index区域。

这样在加载数据的时候,只需加载不超过128KB的RootDataIndex以及IntermediateLevelRootIndex,而避免加载如HFile V1的所有的Leaf index block信息,同样,也只需要加载BloomBlockIndex信息到内存,这样避免在HFile V1格式因为加载过大的DataBlockIndex造成的开销,加快Region的加载速度。

在HFile中根据一个key搜索一个data的过程:

1、先内存中对HFile的root index进行二分查找。如果支持多级索引的话,则定位到的是leaf/intermediate index,如果是单级索引,则定位到的是data block

2、如果支持多级索引,则会从缓存/hdfs(分布式文件系统)中读取leaf/intermediate index chunk,在leaf/intermediate chunk根据key值进行二分查找(leaf/intermediate index chunk支持二分查找),找到对应的data block。

3、从缓存/hdfs中读取data block

4、在data block中遍历查找key。

1、 首先读取文件尾的4字节Version信息(FileTrailer的version字段)。

2、 根据Version信息得到Trailer的长度(不同版本有不同的长度),然后根据trailer长度,加载FileTrailer。

3、 加载load-on-open部分到内存中,起始的文件偏移地址是trailer中的loadOnOpenDataOffset,load-on-open部分长度等于(HFile文件长度 - HFileTrailer长度)

Load-on-open各个部分的加载顺序如下:

依次加载各部分的HFileBlock(load-on-open所有部分都是以HFileBlock格式存储):data index block、meta index block、FileInfo block、generate bloom filter index、和delete bloom filter。HFileBlock的格式会在下面介绍。

在hfile中,所有的索引和数据都是以HFileBlock的格式存在在hdfs中,

HFile version2的Block格式如下两图所示,有两种类型,第一种类型是没有checksum;第二种是包含checksum。对于block,下图中的绿色和浅绿色的内存是block header;深红部分是block data;粉红部分是checksum。

第一种block的header长度= 8 + 2 * 4 + 8;

第二种block的header长度=8 + 2 * 4 + 8 + 1 + 4 * 2;

BlockType:8个字节的magic,表示不同的block 类型。

CompressedBlockSize:表示压缩的block 数据大小(也就是在HDFS中的HFileBlock数据长度),不包括header长度。

UncompressedBlockSize:表示未经压缩的block数据大小,不包括header长度。

PreBlockOffset:前一个block的在hfile中的偏移地址;用于访问前一个block而不用跳到前一个block中,实现类似于链表的功能。

CheckSumType:在支持block checksum中,表示checksum的类型。

bytePerCheckSum:在支持checksum的block中,记录了在checksumChunk中的字节数;records the number of bytes in a checksum chunk。

SizeDataOnDisk:在支持checksum的block中,记录了block在disk中的数据大小,不包括checksumChunk。

DataBlock是用于存储具体kv数据的block,相对于索引和meta(这里的meta是指bloom filter)DataBlock的格式比较简单。

在DataBlock中,KeyValue的分布如下图,在KeyValue后面跟一个timestamp。

HFile中的index level是不固定的,根据不同的数据类型和数据大小有不同的选择,主要有两类,一类是single-level(单级索引),另一类是multi-level(多级索引,索引block无法在内存中存放,所以采用多级索引)。

HFile中的index chunk有两大类,分别是root index chunk、nonRoot index chunk。而nonRoot index chunk又分为interMetadiate index chunk和leaf index chunk,但intermetadiate index chunk和leaf index chunk在内存中的分布是一样的。

对于meta block和bloom block,采用的索引是single-level形式,采用single-level时,只用root index chunk来保存指向block的索引信息(root_index--xxx_block)。

而对于data,当HFile的data block数量较少时,采用的是single level(root_index--data_block)。当data block数量较多时,采用的是multi-level,一般情况下是两级索引,使用root index chunk和leaf index chunk来保存索引信息(root_index--leaf_index--data_block);但当data block数量很多时,采用的是三级索引,使用root index chunk、intermetadiate index chunk和leaf index chunk来保存指向数据的索引(root_index--intermediate_index--leaf_index--data_block)。

所有的index chunk都是以HFileBlock格式进行存放的,首先是一个HFileBlock Header,然后才是index chunk的内容。

Root index适用于两种情况:

1、作为data索引的根索引。

2、作为meta和bloom的索引。

在Hfile Version2中,Meta index和bloom index都是single-level,也都采用root索引的格式。Data index可以single-level和multi-level的这形式。Root index可以表示single-level index也可以表示multi-level的first level。但这两种表示方式在内存中的存储方式是由一定差别,见图。

对于multi-level root index,除了上面index entry数组之外还带有格外的数据mid-key的信息,这个mid-key是用于在对hfile进行split时,快速定位HFile的中间位置所使用。Multi-level root index在硬盘中的格式见图3.4。

Mid-key的信息组成如下:

1、Offset:所在的leaf index chunk的起始偏移量

2、On-disk size:所在的leaf index chunk的长度

3、Key:在leaf index chunk中的位置。

Hbase扩容原理

Hbase是Hadoop的一个存储组件可以提供低延迟的读写操作,它一般构建在HDFS之上,可以处理海量的数据。Hbase有个很好的特性是可以自动分片,也就是意味着当表的数据量变得很大的时候,系统可以自动的分配这些数据。

Hbase的基本存储单位是Region,Region是表数据的子集,多个Region的数据集合可以组成一张完成的表数据。Region本质上存储的一些排好序的,连续的行数据。最初的时候一张表只有一个Region,当Region变得非常大的时候,Region就会从中间分裂成两个基本等大的Region。

在Hbase中,slave也被称作RegionServer,每个RegionServer负责管理一些Region,同时一个Region只能属于一个RegionServer。

一个RegionServer可以服务一个或多个Region,每个Region在Region Server启动的时候被分配。Master可以决定将一些Region从一个RegionServer中移动到令一个RegionServer里面,以便更好的负载均衡。当某个RegionServer故障的时候,Master也可以将它的Region分配给其他的RegionServer。

Region与RegionServer之间的映射关系存储在Zookeeper中的META表中,通过读取META表,你就可以知道那个Region可以负责处理你的rowkey操作,其实这也代表着在HBase读写操作的时候是不用经过Master节点的,你可以之间联系RegionServer。

如图,在客户端进行scan的时候,它可以之间联系多个RegionServer处理当前的操作。

Meta表是用来跟踪Region的,它包含服务器的名称,Region的名称,表名,还有Region的startkey。通过startkey的范围,客户端就可以定位到当前的key要去哪一个Region了。

客户端在请求过META表之后,一般会将表缓存起来,防止每次操作都去获取。在Region进行分裂的时候,客户端去RegionServer操作Region的时候回返回异常,然后客户端会重新获取最新的META表信息。

Hbase的Java客户端API有两个主要的接口:

通过上面介绍,可以知道HBase虽然是Master/Slave架构的,但是并不是每次操作都经过Master的,读写数据的时候HBase只需要直接联系RegionServer即可。这也是HBase可以“无限扩容”的原因。在吞吐量不够的时候,通过增加RegionServer节点,可以增加吞吐量。

HBase 计数器

HBase 高级功能:计数器,可以用于实时统计,而不需要离线批量处理。HBase 有一种机制可以将列当作计数器,支持原子操作,否则用户需要对一行数据加锁,在进行读取更新操作,会引起大量的资源竞争问题。

HBase Client API 提供了专门的方法完整这种读取并修改的操作,在单独的一次客户端调用中 保证原子性 。早期的 HBase 版本会在每次计数器更新操作调用一次 RPC 请求,新版本中可以在一次 RPC 请求中完成多个计数器的更新操作,但是多个计数器必须在同一行。

计数器不需要初始化,创建一个新列时初始值为0,第一次 incr 操作返回1。

计数器使用 incr 命令,增量可以是正数也可以是负数,但是必须是长整数 Long:

计数器使用的例子:

需要注意的是,增加的参数必须是长整型 Long,如果按照错误的格式更新了计数器(如字符串格式),下次调用 incr 会得到错误的结果:

操作一个计数器,类似 shell 命令 incr

使用 Table 的 increment() 方法可以操作一行的多个计数器,需要构建 Increment 实例,并且指定行键:

Increment 类还有一种构造器:

rowLock 参数可选,可以设置用户自定义锁,可以限制其他写程序操作此行,但是不保证读的操作性。

References:

《HBase 权威指南》

HBase服务高可用之路的探索

[toc]

这里的高可用并不是指HBase本身的高可用机制。而是HBase主备双服务的高可用,线上业务依赖于主备HBase集群来提供数据支持,主集群首要的任务时负责数据的读写,备集群只是为了容灾。

对于HBase主备服务高可用方案的调研,团队内部从未停止过探索的步伐。从最初手动切换Nginx的域名映射,到统计异常日志占比,然后进行自动的域名切换。那时候我们面临的状况是,主集群大量读写超时、甚至服务不可用,造成业务方接口无法为用户提供正常的线上业务时,HBase运维小伙伴们才能感知到HBase集群的异常状态,手动切换流量至备集群,从而在服务恢复的时间内,造成了无法容忍的损失。

针对旧方案的种种痛点,以及受微服务中熔断概念的启发,最终选择集成了饿了么提供的一个熔断框架—— doctor ,实现了HBase主集群服务查询异常时,查询流量能够及时、自动、无感知地进行切换到备集群。

一般在微服架构中,有一个组件角色叫熔断器。顾名思义,熔断器起的作用就是在特定的场景下关掉当前的通路,从而起到保护整个系统的效果。

在微服务架构中,一般我们的独立服务是比较多的,每个独立服务之间划分责任边界,并通过约定协议接口来进行通信。当我们的调用链路复杂依赖多时,很可能会发生雪崩效应。

假设有这么一个场景,有A, B, C, D四个独立服务,A会依赖B,C,D;当D发生负载过高或网络异常等导致响应过慢或超时时,很可能A会因此堆积过多的等待链接,从而导致A的状态也转为异常,后面依赖到A的其他服务跟着发生链式反应,这将会导致大面积的服务不可用,即使本来是一些没有依赖到B,C,D的服务。如下图所示:

这不是我们希望看到的结果,所以这个时候熔断器可以派上用场。最简单的做法,我们为每个依赖服务配置一个熔断器开关,正常情况下是关闭的,也就是可以正常发起请求;当请求失败(超时或者其他异常)次数超过预设值时,熔断器自动打开,这时所有经过这个熔断器的请求都会直接返回失败,并没有真正到达所依赖的服务上。这时服务A本身仍然是能正常服务的。当然,我们针对失败请求的策略,并没有这么简单粗暴。

HBase 虽然提供了 HBase Replication 机制,用来实现集群间单方向的异步数据复制,线上虽然部署了双集群,备集群 SSD 分组和主集群 SSD 分组有相同的配置。当主集群因为磁盘,网络,或者其他业务突发流量影响导致某些 RegionServer 甚至集群不可用的时候,就需要提供备集群继续提供服务,备集群的数据可能会因为 HBase Replication 机制的延迟,相比主集群的数据是滞后的,按照我们集群目前的规模统计,平均延迟在 100ms 以内。所以为了达到高可用,业务方只能接受复制延迟,放弃强一致性,选择最终一致性和高可用性。

有赞技术团队对于HBase高可用服务接口的设计,同样使用了熔断的概念,只是其底层的熔断技术依赖于java微服务中的Hystrix框架。其简单的客户端高可用方案原理图如下所示:

业务方是不想感知到后端服务的状态,也就是说在客户端层面,他们只希望一个 Put 或者 Get 请求正常送达且返回预期的数据即可,那么就需要高可用客户端封装一层降级,熔断处理的逻辑,这里有赞采用 Hystrix 做为底层熔断处理引擎,在引擎之上封装了 HBase 的基本 API,用户只需要配置主备机房的 ZK 地址即可,所有的降级熔断逻辑最终封装到 ha-hbase-client 中。

以上文字描述摘选自有赞的技术博客,详情可以参考链接, 有赞 HBase 技术实践:读流程解析与优化

与微服务中的熔断概念类比,我们也可以把我们的主备HBase集群看做是两个独立的服务,而我们的业务方则需要依赖这一个HBase服务,对外提供自己的服务。这里稍微有一点不一样的地方是,我们HBase服务的角色是由两个集群来担任,正常情况下,只有一个集群来承担起HBase服务的功能。HBase熔断切换的简单示例如下:

如果想要更深入地理解主备熔断切换的设计理念,那么,需要优先理解一下滚动窗口计数,以及阈值判断相关的一些内容。 doctor 熔断框架的设计中,依赖于滑动窗口时间内的滚动计数,来进行阈值计算,从而判断当前服务的健康状况。

滚动计数的行为类似于一个拥有固定长度的先进先出队列,或者时间戳序列上的滑动窗口。一个滚动计数的值是队列元素的和,时钟结束时,最后一个元素的值将滚动到先前的位置,传递了一个时间粒度,这个时间粒度,默认1s。下面将借助一个小例子,具体来说明这种机制。

上述便是对HBase熔断思想所做的一个由浅入深的解释,用于实现业务方访问HBase时,对于主备HBase集群的状态切换无感知。即使主集群处于异常状态,我们依旧可以为业务方提供正常的HBase服务。

HBase 写性能优化

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

HBase数据写入通常会遇到两类问题,一类是写性能较差,另一类是数据根本写不进去。这两类问题的切入点也不尽相同,如下图所示:

优化原理:数据写入流程可以理解为一次顺序写WAL+一次写缓存,通常情况下写缓存延迟很低,因此提升写性能就只能从WAL入手。WAL机制一方面是为了确保数据即使写入缓存丢失也可以恢复,另一方面是为了集群之间异步复制。默认WAL机制开启且使用同步机制写入WAL。首先考虑业务是否需要写WAL,通常情况下大多数业务都会开启WAL机制(默认),但是对于部分业务可能并不特别关心异常情况下部分数据的丢失,而更关心数据写入吞吐量,比如某些推荐业务,这类业务即使丢失一部分用户行为数据可能对推荐结果并不构成很大影响,但是对于写入吞吐量要求很高,不能造成数据队列阻塞。这种场景下可以考虑关闭WAL写入,写入吞吐量可以提升2x~3x。退而求其次,有些业务不能接受不写WAL,但可以接受WAL异步写入,也是可以考虑优化的,通常也会带来1x~2x的性能提升。

优化推荐:根据业务关注点在WAL机制与写入吞吐量之间做出选择

其他注意点:对于使用Increment操作的业务,WAL可以设置关闭,也可以设置异步写入,方法同Put类似。相信大多数Increment操作业务对WAL可能都不是那么敏感~

优化原理:HBase分别提供了单条put以及批量put的API接口,使用批量put接口可以减少客户端到RegionServer之间的RPC连接数,提高写入性能。另外需要注意的是,批量put请求要么全部成功返回,要么抛出异常。

优化建议:使用批量put进行写入请求

优化原理:业务如果可以接受异常情况下少量数据丢失的话,还可以使用异步批量提交的方式提交请求。提交分为两阶段执行:用户提交写请求之后,数据会写入客户端缓存,并返回用户写入成功;当客户端缓存达到阈值(默认2M)之后批量提交给RegionServer。需要注意的是,在某些情况下客户端异常的情况下缓存数据有可能丢失。

优化建议:在业务可以接受的情况下开启异步批量提交

使用方式:setAutoFlush(false)

优化原理:当前集群中表的Region个数如果小于RegionServer个数,即Num(Region of Table) Num(RegionServer),可以考虑切分Region并尽可能分布到不同RegionServer来提高系统请求并发度,如果Num(Region of Table) Num(RegionServer),再增加Region个数效果并不明显。

优化建议:在Num(Region of Table) Num(RegionServer)的场景下切分部分请求负载高的Region并迁移到其他RegionServer;

优化原理:另一个需要考虑的问题是写入请求是否均衡,如果不均衡,一方面会导致系统并发度较低,另一方面也有可能造成部分节点负载很高,进而影响其他业务。分布式系统中特别害怕一个节点负载很高的情况,一个节点负载很高可能会拖慢整个集群,这是因为很多业务会使用Mutli批量提交读写请求,一旦其中一部分请求落到该节点无法得到及时响应,就会导致整个批量请求超时。因此不怕节点宕掉,就怕节点奄奄一息!

优化建议:检查RowKey设计以及预分区策略,保证写入请求均衡。

KeyValue大小对写入性能的影响巨大,一旦遇到写入性能比较差的情况,需要考虑是否由于写入KeyValue数据太大导致。KeyValue大小对写入性能影响曲线图如下:

图中横坐标是写入的一行数据(每行数据10列)大小,左纵坐标是写入吞吐量,右坐标是写入平均延迟(ms)。可以看出随着单行数据大小不断变大,写入吞吐量急剧下降,写入延迟在100K之后急剧增大。

说到这里,有必要和大家分享两起在生产线环境因为业务KeyValue较大导致的严重问题,一起是因为大字段业务写入导致其他业务吞吐量急剧下降,另一起是因为大字段业务scan导致RegionServer宕机。

案件一:大字段写入导致其他业务吞吐量急剧下降

部分业务反馈集群写入忽然变慢、数据开始堆积的情况,查看集群表级别的数据读写QPS监控,发现问题的第一个关键点:业务A开始写入之后整个集群其他部分业务写入QPS都几乎断崖式下跌,初步怀疑黑手就是业务A。

下图是当时业务A的写入QPS(事后发现脑残忘了截取其他表QPS断崖式下跌的惨象),但是第一感觉是QPS并不高啊,凭什么去影响别人!

于是就继续查看其他监控信息,首先确认系统资源(主要是IO)并没有到达瓶颈,其次确认了写入的均衡性,直至看到下图,才追踪到影响其他业务写入的第二个关键点:RegionServer的handler(配置150)被残暴耗尽:

对比上面两张图,是不是发现出奇的一致,那就可以基本确认是由于该业务写入导致这台RegionServer的handler被耗尽,进而其他业务拿不到handler,自然写不进去。那问题来了,为什么会这样?正常情况下handler在处理完客户端请求之后会立马释放,唯一的解释是这些请求的延迟实在太大。

试想,我们去汉堡店排队买汉堡,有150个窗口服务,正常情况下大家买一个很快,这样150个窗口可能只需要50个服务。假设忽然来了一批大汉,要定制超大汉堡,好了,所有的窗口都工作起来,而且因为大汉堡不好制作导致服务很慢,这样必然会导致其他排队的用户长时间等待,直至超时。

可回头一想这可是写请求啊,怎么会有这么大的请求延迟!和业务方沟通之后确认该表主要存储语料库文档信息,都是平均100K左右的数据,是不是已经猜到了结果,没错,就是因为这个业务KeyValue太大导致。KeyValue太大会导致HLog文件写入频繁切换、flush以及compaction频繁触发,写入性能急剧下降。

目前针对这种较大KeyValue写入性能较差的问题还没有直接的解决方案,好在社区已经意识到这个问题,在接下来即将发布的下一个大版本HBase 2.0.0版本会针对该问题进行深入优化,详见 HBase MOB ,优化后用户使用HBase存储文档、图片等二进制数据都会有极佳的性能体验。

案件二:大字段scan导致RegionServer宕机

案件现场:有段时间有个0.98集群的RegionServer经常频繁宕机,查看日志是由于”java.lang.OutOfMemoryError: Requested array size exceeds VM limit”,如下图所示:

原因分析:通过查看源码以及相关文档,确认该异常发生在scan结果数据回传给客户端时由于数据量太大导致申请的array大小超过JVM规定的最大值( Interge.Max_Value-2)。造成该异常的两种最常见原因分别是:

有的童鞋就要提问啦,说如果已经对返回结果大小做了限制,在表列太宽的情况下是不是就可以不对列数量做限制呢。这里需要澄清一下,如果不对列数据做限制,数据总是一行一行返回的,即使一行数据大小大于设置的返回结果限制大小,也会返回完整的一行数据。在这种情况下,如果这一行数据已经超过array大小阈值,也会触发OOM异常。

解决方案:目前针对该异常有两种解决方案,其一是升级集群到1.0,问题都解决了。其二是要求客户端访问的时候对返回结果大小做限制(scan.setMaxResultSize(2 1024 1024))、并且对列数量做限制(scan.setBatch(100)),当然,0.98.13版本以后也可以对返回结果大小在服务器端进行限制,设置参数hbase.server.scanner.max.result.size即可

上述几点主要针对写性能优化进行了介绍,除此之外,在一些情况下还会出现写异常,一旦发生需要考虑下面两种情况(GC引起的不做介绍):

问题解析:以RegionServer级别flush进行解析,HBase设定一旦整个RegionServer上所有Memstore占用内存大小总和大于配置文件中upperlimit时,系统就会执行RegionServer级别flush,flush算法会首先按照Region大小进行排序,再按照该顺序依次进行flush,直至总Memstore大小低至lowerlimit。这种flush通常会block较长时间,在日志中会发现“Memstore is above high water mark and block 7452 ms”,表示这次flush将会阻塞7s左右。

问题检查点:

问题解析:对于数据写入很快的集群,还需要特别关注一个参数:hbase.hstore.blockingStoreFiles,此参数表示如果当前hstore中文件数大于该值,系统将会强制执行compaction操作进行文件合并,合并的过程会阻塞整个hstore的写入。通常情况下该场景发生在数据写入很快的情况下,在日志中可以发现”Waited 3722ms on a compaction to clean up ‘too many store files“

问题检查点:

上文已经从写性能优化以及写异常诊断两个方面对HBase中数据写入可能的问题进行了详细的解释,相信在0.98版本的基础上对写入来说已经是最好的解决方案了。但是有些业务可能依然觉得不够快,毕竟”更快”是所有存储系统活着的动力,那还有提高空间吗?当然,接下来简单介绍HBase之后版本对写性能优化的两点核心改进:

这个特性意味着可以将WAL单独置于SSD上,这样即使在默认情况下(WALSync),写性能也会有很大的提升。需要注意的是,该特性建立在HDFS 2.6.0+的基础上,HDFS以前版本不支持该特性。具体可以参考官方jira:

该特性也是对WAL进行改造,当前WAL设计为一个RegionServer上所有Region共享一个WAL,可以想象在写入吞吐量较高的时候必然存在资源竞争,降低整体性能。针对这个问题,社区小伙伴(阿里巴巴大神)提出Multiple WALs机制,管理员可以为每个Namespace下的所有表设置一个共享WAL,通过这种方式,写性能大约可以提升20%~40%左右。具体可以参考官方jira:

(责任编辑:IT教学网)

更多

相关广告特效文章

推荐广告特效文章