Fork me on GitHub

Hbase| 02 原理| rowkey设计| 优化

 

Google 发表 GFS、MapReduce、BigTable 三篇论文,号称“三驾马车”,开启了大数据的时代。

 GFS 对应的 Hadoop 分布式文件系统 HDFS,以及 MapReduce 对应的 Hadoop 分布式计算框架 MapReduce,BigTable 对应的 NoSQL 系统 HBase,看看它是如何大规模处理海量

数据的。

在计算机数据存储领域,一直是关系数据库(RDBMS)的天下,以至于在传统企业的应用领域,许多应用系统设计都是面向数据库设计,也就是先设计数据库然后设计程序,从而导

关系模型绑架对象模型,并由此引申出旷日持久的业务对象贫血模型与充血模型之争。

从 Google 的 BigTable 开始,一系列的可以进行海量数据存储与访问的数据库被设计出来,更进一步说,NoSQL 这一概念被提了出来。

NoSQL,主要指非关系的、分布式的、支持海量数据存储的数据库设计模式。也有许多专家将 NoSQL 解读为 Not Only SQL,表示 NoSQL 只是关系数据库的补充,而不是替代方

案。其中,HBase 是这一类 NoSQL 系统的杰出代表。

HBase 之所以能够具有海量数据处理能力,其根本在于和传统关系型数据库设计的不同思路。传统关系型数据库对存储在其上的数据有很多约束,学习关系数据库都要学习数据库设

计范式,事实上,是在数据存储中包含了一部分业务逻辑。而 NoSQL 数据库则简单暴力地认为,数据库就是存储数据的,业务逻辑应该由应用程序去处理,有时候不得不说,简单暴

力也是一种美。

 

HBase的设计目标就是为了那些巨大的表,如数十亿行、数百万列。

面向列,准确的说是面向列族。每行数据列可以不同。

1. 可伸缩架构

  HBase 为可伸缩海量数据储存而设计,实现面向在线业务的实时数据访问延迟。HBase 的伸缩性主要依赖其可分裂的 HRegion 及可伸缩的分布式文件系统 HDFS 实现。

 

HRegion 是 HBase 负责数据存储的主要进程,应用程序对数据的读写操作都是通过和 HRegion 通信完成。上面是 HBase 架构图,我们可以看到在 HBase 中,数据以 HRegion 为单

位进行管理,也就是说应用程序如果想要访问一个数据,必须先找到 HRegion,然后将数据读写操作提交给 HRegion,由 HRegion 完成存储层面的数据操作。

HRegionServer 是物理服务器,每个 HRegionServer 上可以启动多个 HRegion 实例。当一个 HRegion 中写入的数据太多,达到配置的阈值时,一个 HRegion 会分裂成两个

HRegion,并将 HRegion 在整个集群中进行迁移,以使 HRegionServer 的负载均衡。

每个 HRegion 中存储一段 Key 值区间 [key1, key2) 的数据,所有 HRegion 的信息,包括存储的 Key 值区间、所在 HRegionServer 地址、访问端口号等,都记录在 HMaster 服务器

上。为了保证 HMaster 的高可用,HBase 会启动多个 HMaster,并通过 ZooKeeper 选举出一个主服务器。

下面是一张调用时序图,应用程序通过 ZooKeeper 获得主 HMaster 的地址,输入 Key 值获得这个 Key 所在的 HRegionServer 地址,然后请求 HRegionServer 上的 HRegion,获得

所需要的数据。

 

 

数据写入过程也是一样,需要先得到 HRegion 才能继续操作。HRegion 会把数据存储在若干个 HFile 格式的文件中,这些文件使用 HDFS 分布式文件系统存储,在整个集群内分布并

高可用。当一个 HRegion 中数据量太多时,这个 HRegion 连同 HFile 会分裂成两个 HRegion,并根据集群中服务器负载进行迁移。如果集群中有新加入的服务器,也就是说有了新的

HRegionServer,由于其负载较低,也会把 HRegion 迁移过去并记录到 HMaster,从而实现 HBase 的线性伸缩。

先小结一下上面的内容,HBase 的核心设计目标是解决海量数据的分布式存储,和 Memcached 这类分布式缓存的路由算法不同,HBase 的做法是按 Key 的区域进行分片,这个分片

也就是 HRegion。应用程序通过 HMaster 查找分片,得到 HRegion 所在的服务器 HRegionServer,然后和该服务器通信,就得到了需要访问的数据。

HBase 可扩展数据模型

传统的关系数据库为了保证关系运算(通过 SQL 语句)的正确性,在设计数据库表结构的时候,需要指定表的 schema 也就是字段名称、数据类型等,并要遵循特定的设计范式。这些规范带来了一个问题,就是僵硬的数据结构难以面对需求变更带来的挑战,有些应用系统设计者通过预先设计一些冗余字段来应对,但显然这种设计也很糟糕。

那有没有办法能够做到可扩展的数据结构设计呢?不用修改表结构就可以新增字段呢?当然有的,许多 NoSQL 数据库使用的列族(ColumnFamily)设计就是其中一个解决方案。列族最早在 Google 的 BigTable 中使用,这是一种面向列族的稀疏矩阵存储格式,如下图所示。

  

 

 

这是一个学生的基本信息表,表中不同学生的联系方式各不相同,选修的课程也不同, 而且将来还会有更多联系方式和课程加入到这张表里,如果按照传统的关系数据库设计,无论提前预设多少冗余字段都会捉襟见肘、疲于应付。

而使用支持列族结构的 NoSQL 数据库,在创建表的时候,只需要指定列族的名字,无需指定字段(Column)。那什么时候指定字段呢?可以在数据写入时再指定。通过这种方式,数据表可以包含数百万的字段,这样就可以随意扩展应用程序的数据结构了。并且这种数据库在查询时也很方便,可以通过指定任意字段名称和值进行查询。

HBase 这种列族的数据结构设计,实际上是把字段的名称和字段的值,以 Key-Value 的方式一起存储在 HBase 中。实际写入的时候,可以随意指定字段名称,即使有几百万个字段也能轻松应对。

HBase 的高性能存储

传统的机械式磁盘的访问特性是连续读写很快,随机读写很慢。这是因为机械磁盘靠电机驱动访问磁盘上的数据,电机要将磁头落到数据所在的磁道上,这个过程需要较长的寻址时

间。如果数据不连续存储,磁头就要不停的移动,浪费了大量的时间。

为了提高数据写入速度,HBase 使用了一种叫作LSM 树的数据结构进行数据存储。LSM 树的全名是 Log Structed Merge Tree,翻译过来就是 Log 结构合并树。数据写入的时候以

Log 方式连续写入,然后异步对磁盘上的多个 LSM 树进行合并。

LSM 树可以看作是一个 N 阶合并树。数据写操作(包括插入、修改、删除)都在内存中进行,并且都会创建一个新记录(修改会记录新的数据值,而删除会记录一个删除标志)。这

些数据在内存中仍然还是一棵排序树,当数据量超过设定的内存阈值后,会将这棵排序树和磁盘上最新的排序树合并。当这棵排序树的数据量也超过设定阈值后,会和磁盘上下一级的

排序树合并。合并过程中,会用最新更新的数据覆盖旧的数据(或者记录为不同版本)。

在需要进行读操作时,总是从内存中的排序树开始搜索,如果没有找到,就从磁盘 上的排序树顺序查找。

在 LSM 树上进行一次数据更新不需要磁盘访问,在内存即可完成。当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用 LSM 树可以极大程度地减少磁盘的访问

次数,加快访问速度。

以上的总结:

HBase 作为 Google BigTable 的开源实现,完整地继承了 BigTable 的优良设计。架构上通过数据分片的设计配合 HDFS,实现了数据的分布式海量存储;数据结构上通过列族的设计,实现了数据表结构可以在运行期自定义;存储上通过 LSM 树的方式,使数据可以通过连续写磁盘的方式保存数据,极大地提高了数据写入性能。

 

 

 

 

HMaster会到ZK中进行注册,ZK中一主二备; 当主宕机时,zk通知备机, 备机中选择一个当主机;

HRegionServer负责数据的存储读写;

HMaster做负载均衡数据均匀的分配给HRegionServer,它负责管理HRegionServer;

HLog-WAL(预写日志,日志格式为HLog)记录对数据的增删改,写数据之前先记录在HLog(它也不是实时的同步)里边,在小的时间间隔做同步到 DataNode

1个Region-----对应1个Hbase表一个表可以一个或有多个HRegion,一个HRegion不能存储多个表;

1个store-------对应1个列族;每个store存放一个列族

把内存中数据先存放在--> Mem Store,达到阈值--StoreFile(HFile的格式)放到--->HDFS Client-->DataNode

列是最小单元

Hbase处理10亿行,百万列数据是先把hbase表会做水平切分,每一份放一个region;

列族数<=2-3个,不然等到它的flush机制,刷写的列族会很多;

 

HBase是由Client、Zookeeper、Master、HRegionServer、HDFS等几个组件组成

①Client

Client包含了访问HBase的接口,另外Client还维护了对应的cache来加速Hbase的访问,比如cache的.META.元数据的信息。

②Zookeeper:

HBase通过Zookeeper来做Master的高可用、RegionServer的监控(容灾性)、元数据的入口以及集群配置的维护等工作。具体工作如下:
(1)通过Zoopkeeper来保证集群中只有1个Master在运行,如果Master异常,会通过竞争机制产生新的Master提供服务
(2)通过Zoopkeeper来监控RegionServer的状态,当RegionSevrer有异常的时候,通过回调的形式通知Master RegionServer上下线的信息,master会把宕机的数据给恢复了给其他RegionServer
(3)通过Zoopkeeper存储元数据的统一入口地址

③ Master

Master节点的主要职责如下:

监控RegionServer| 处理RegionServer故障转移| 通过Zookeeper发布自己的位置给客户端

(1)为RegionServer分配Region;
(2)维护整个集群的负载均衡;
(3)维护集群的元数据信息;(有多少个RegionServer,存了多少个Region,每个Region存多大数据)
(4)发现失效的Region,并将失效的Region分配到正常的RegionServer上;
(5)当RegionSever失效的时候,协调对应Hlog的拆分;

④ RegionServer

HregionServer负责存储HBase的实际数据|维护Hlog| 执行压缩

。它的功能概括如下:

(1)管理master为其分配的Region;
(2)处理来自客户端的读写请求;
(3)负责和底层HDFS的交互,存储数据到HDFS(刷新缓存到HDFS);
(4)负责Region变大以后的拆分(分片);
(5)负责Storefile的合并工作;

⑤ HDFS

HDFS为HBase提供最终的底层数据存储服务,同时为HBase提供高可用(Hlog存储在HDFS)的支持,具体功能概括如下:

(1)提供元数据和表数据的底层分布式存储服务
(2)数据多副本,保证的高可靠和高可用性

Write-Ahead logfile

HBase的修改记录,当对HBase写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问

题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中

Region

Hbase表的分片,HBase表会根据RowKey值被切分成不同的region存储在RegionServer中,在一个RegionServer中可以有多个不同的region。

Store

HFile存储在Store中,一个Store对应HBase表中的一个列族

MemStore

就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在WAL中之后,RegsionServer会在内存中存储键值对。

StoreFile 

这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile是以Hfile的形式存储在HDFS的。Hfile是一种文件格式,例如txt、orc、parquet等

RowKey 

是用来检索记录的主键。访问HBASE table中的行,只有三种方式:

  1)通过单个RowKey访问
  2)通过RowKey的range(正则)
  3)全表扫描

在HBASE内部,RowKey保存为字节数组。存储时,数据按照RowKey的字典序(byte order)排序存储。设计RowKey时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性) ;  

hbase中的rowkey 三大设计原则: 唯一性、长度原则、散列原则

 

Column Family

列族:HBASE表中的每个列,都归属于某个列族。列族是表的schema的一部 分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于courses 这个列族。

Cell 

{rowkey, column Family:column} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。

Time Stamp

版本通过时间戳来索引,为64位长整型;

不同版本的数据按照时间倒序排序,即最新的数据排在最前面。

命名空间

HBase命名空间 namespace 是与关系数据库系统中的数据库类似的表的逻辑分组

2. HBase原理

元数据有2类:

master 有几个regionserver,regionserver中有几个region,region中的数据量有多大

regionserver,hbase所有数据存储的位置,对应rowkey

读流程

 
读数据rowkey存到哪里了?
client-->zk(meta-region-server)
它存储元数据所在位置,meta存在RS中

[zk: localhost:2181(CONNECTED) 9] get /hbase/meta-region-server
�regionserver:16020+��PBUF

        hadoop101

scan 'hbase:meta'
column=info:server, timestamp=1551919444973, value=hadoop101:16020 
column=info:regioninfo,timestamp=1551919444973, value={ENCODED=>XX, NAME=>'student,XXX', STARTKEY =>'', ENDKEY => ''}
一张表可能分布在不同region中,meta中记录这个region是从哪一行到哪结束;


存储时会有一个meta
rowkey存放到哪个RS中
client第一次读取会缓存rowkey在哪
 

写流程

 

Mem Store内存节点,预写日志WAL,格式Hlog

  • ①先写入HLog(磁盘) 
  • ②再写入Mem Store中
  • ③如果HLog落盘失败,Mem Store就会回滚(这两个操作在一个事务中),请求写数据失败,保证了数据的一致性

数据flush过程

Flush:

1) region级别的flush

  • Memstore级别限制:当Region中任意一个MemStore的大小达到了上限(hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新。
  • 当Region中所有Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新
  • HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。

Mem Store

  • ①单个默认128M 刷新;
  • ②region中有多个Mem Store,region中所有的MamStore达到128*2,触发整个region的flush
  • ③memstore数据存储超过1h刷新。

flush刷写最小单位是region而不是MemStore;而判断标准是region

2) Regionserver级别的flush 

Regionserver中会有多个 Region的 Memstore,

Memstore的大小总和达到了上限:默认 40% * JVM堆内存---> 开始flush,并阻塞写操作(flush顺序:按照Memstore由大到小执行);

Memstore的总体内存下限:刷到低点( JVM堆 * 40%  * 95% 即 JVM堆内存 * 38%  )就停止flush,不再阻塞写操作;

  手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。

Memstore Flush流程

为了减少flush过程对读写的影响,HBase采用了类似于两阶段提交的方式,将整个flush过程分为三个阶段:

  1. prepare阶段:遍历当前Region中的所有Memstore,将Memstore中当前数据集kvset做一个快照snapshot,然后再新建一个新的kvset。后期的所有写入操作都会写入新的kvset中,而整个flush阶段读操作会首先分别遍历kvset和snapshot,如果查找不到再会到HFile中查找。prepare阶段需要加一把updateLock对写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。
  2. flush阶段:遍历所有Memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程因为涉及到磁盘IO操作,因此相对比较耗时。
  3. commit阶段:遍历所有的Memstore,将flush阶段生成的临时文件移到指定的ColumnFamily目录下,针对HFile生成对应的storefile和Reader,把storefile添加到HStore的storefiles列表中,最后再清空prepare阶段生成的snapshot。

Memstore Flush对业务读写的影响

上文介绍了HBase在什么场景下会触发flush操作以及flush操作的基本流程,想必对于HBase用户来说,最关心的是flush行为会对读写请求造成哪些影响以及如何避免。因为不同触发方式下的flush操作对用户请求影响不尽相同,因此下面会根据flush的不同触发方式分别进行总结,并且会根据影响大小进行归类:

影响甚微

正常情况下,大部分Memstore Flush操作都不会对业务读写产生太大影响,比如这几种场景:HBase定期刷新Memstore、手动执行flush操作、触发Memstore级别限制、触发HLog数量限制以及触发Region级别限制等,这几种场景只会阻塞对应Region上的写请求,阻塞时间很短,毫秒级别。

影响较大

然而一旦触发Region Server级别限制导致flush,就会对用户请求产生较大的影响。会阻塞所有落在该Region Server上的更新操作,阻塞时间很长,甚至可以达到分钟级别。一般情况下Region Server级别限制很难触发,但在一些极端情况下也不排除有触发的可能,下面分析一种可能触发这种flush操作的场景:

相关JVM配置以及HBase配置:

maxHeap = 71
hbase.regionserver.global.memstore.upperLimit = 0.35
hbase.regionserver.global.memstore.lowerLimit = 0.30

基于上述配置,可以得到触发Region Server级别的总Memstore内存和为24.9G,如下所示:

2015-10-12 13:05:16,232 INFO  [regionserver60020] regionserver.MemStoreFlusher: globalMemStoreLimit=24.9 G, globalMemStoreLimitLowMark=21.3 G, maxHeap=71 G

假设每个Memstore大小为默认128M,在上述配置下如果每个Region有两个Memstore,整个Region Server上运行了100个region,根据计算可得总消耗内存 = 128M * 100 * 2 = 25.6G > 24.9G,很显然,这种情

况下就会触发Region Server级别限制,对用户影响相当大。

根据上面的分析,导致触发Region Server级别限制的因素主要有一个Region Server上运行的Region总数,一个是Region上的Store数(即表的ColumnFamily数)。对于前者,根据读写请求量一般建议线上一个

Region Server上运行的Region保持在50~80个左右,太小的话会浪费资源,太大的话有可能触发其他异常;对于后者,建议ColumnFamily越少越好,如果从逻辑上确实需要多个ColumnFamily,最好控制在3个

以内。

数据切分和合并过程

split: 

1)一张表可能有一个或多个region; 随着数据量增加, store会把数据分到另外一个region中,这样数据量就会减半,并发量增加,提高了读写性能;

store(1个store对应HBase中1个列族)分裂最大体量10G

一个点越长越多,而不是越长越大;

接受master调度,使region在regionserver中均匀的分布

HBase通常会根据hbase-default.xml和hbase-site.xml 配置文件中的设置来处理您所在区域的分割。重要的设置包括:hbase.regionserver.region.split.policy,hbase.hregion.max.filesize,

hbase.regionserver.regionSplitLimit。分割的一个简单的观点是,当一个区域发展到hbase.hregion.max.filesize时,它被分割。对于大多数使用模式,您应该使用自动分割。

查看IncreasingToUpperBoundRegionSplitPolicy,可知切分规则:

 
Math.min(tableRegionsCount^3 * initialSize,defaultRegionMaxFileSize)
1) tableRegionCount:当前表在所有RegionServer上拥有的所有的Region数量的总和
2) initialSize:如果定义了hbase.increasing.policy.initial.size,则使用该值,否则用memstore刷写值得2倍,即hbase.hregion.memstore.flush.size*2
3) defaultRegionmaxFileSize: hbase.hregion.max.filesize,也就是Region的最大大小
4) Math.min:取这两个数值的最小值

例如当初始hbase.hregion.memstore.flush.size定义为128M,过程为:
1)当只有一个Region,min(256M,10G),也就是说store File达到256M触发分裂;(2*128,10G)
2)当有2个Region,min(2048M,10G), store File达到2048M触发分裂;(2^3*128,10G)
3)当有3个Region,min(6912M,10G), store File达到6912M触发分裂;
4)当有4个Region时候,为min(16G,10G),上限达到了10GB,store File达到10G就触发分裂,此后,Region数量再增加也不会增加上限

 

涉及到的相关配置有(hbase-site.xml中配置):
拆分策略的初始化大小
hbase.increasing.policy.initial.size
memstore最大刷写值
hbase.hregion.memstore.flush.size
固定大小拆分策略定义的最大Region大小
hbase.hregion.max.filesize

compact:合并

分裂足够多了就会合并;

1)每隔7day, 每个region的所有file会进行合并,mamstore写出生成一个file;

2)store每次flush都会生成一个storefile,达到3个时将触发合并;拆的太散就合

 

3. HBase优化

高可用

在HBase中Hmaster负责监控RegionServer的生命周期,均衡RegionServer的负载,如果Hmaster挂掉了,那么整个HBase集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以HBase支持对Hmaster的高可用配置。

在conf目录下创建backup-masters文件;在backup-masters文件中配置高可用HMaster节点
    [kris@hadoop101 conf]$ vim backup-masters
      hadoop102
将整个conf目录scp到其他节点
    xsync backup-masters

预分区

每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。

预分区(结合rowkey的设计用)是为了解决:

①热点问题:
  多个客户端读写一个regionserver
②切分(数据量大时)时会影响读写:

手动设定预分区

 
hbase> create 'staff1','info',SPLITS => ['10','20','30','40']

三种:
00-10
10-20
20-30
30-40
一共分了5个区
hbase(main):005:0> put 'staff', '2001', 'info:name', 'alex'
0 row(s) in 0.1050 seconds

2001是被分到了20-30这个区,因为它是按照字典顺序排序的;先比较第一个,再比较第二个...
 

 

 
按照文件中设置的规则预分区
创建splits.txt文件内容如下:
[kris@hadoop101 hbase-1.3.1]$ vim splits.txt 
aaaa
bbbb
cccc
dddd
然后执行:
create 'staff2','info',SPLITS_FILE => 'splits.txt'
 

 
生成16进制序列预分区
create 'staff3','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

                11111111
11111111    22222222
22222222    33333333
...
dddddddd    eeeeeeee
eeeeeeee
 

使用JavaAPI创建预分区

 
//自定义算法,产生一系列Hash散列值存储在二维数组中
byte[][] splitKeys = 某个散列值函数
//创建HBaseAdmin实例
HBaseAdmin hAdmin = new HBaseAdmin(HBaseConfiguration.create());
//创建HTableDescriptor实例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通过HTableDescriptor实例和散列值二维数组创建带有预分区的HBase表
hAdmin.createTable(tableDesc, splitKeys);
 

 

预分区:
create  'dmp_wanka',
{NAME => 'info',BLOOMFILTER => 'ROWCOL',COMPRESSION => 'SNAPPY'},
{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'},
{NAME => 'app',BLOOMFILTER => 'ROWCOL',COMPRESSION => 'SNAPPY'},
{NUMREGIONS => 9, SPLITALGO => 'HexStringSplit'}

 

RowKey设计

设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。解决热点问题,均匀分布

 rowkey,不要超过16字节;数据是以k v形式存储在hfile,过多占空间内存,影响Hfile的存储效率; memstore要缓存部分数据到内存,也会占用内存空间。使得内存利用率降低;

设计原则: 唯一性 、散列性、 长度

1.哈希

哈希会使同一rowkey的数据永远在同一个region中。哈希也可以使负载分散到整个集群,但是读却是可以预测的,可以使用get操作准确获取某一个行数据

比如:
原本rowKey为1001的,哈希后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
原本rowKey为3001的,哈希后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
原本rowKey为5001的,哈希后变成:7b61dec07e02c188790670af43e717f0f46e8913

在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowKey来Hash后作为每个分区的临界值

字符串反转

反转行键固定宽度或数字,以便最经常改变的部分在第一位。这有效地使行键随机化,但牺牲了行排序属性。

20170524000001转成10000042507102
20170524000002转成20000042507102

时间戳反转 -取反

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用3.时间戳反转 -取反

可以用Long.Max_Value - timestamp追加到rowkey的末尾

例如[rowkey][ Long.Max_Value - timestamp],[rowkey]的最新值可以通过scan [rowkey]获得[rowkey]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计 [userId反转][Long.Max_Value - timestamp],在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId反转][000000000000],stopRow是[userId反转][Long.Max_Value - timestamp] 。

如果需要查询某段时间的操作记录,startRow是 [userId反转][Long.Max_Value - 结束时间],stopRow是[userId反转] [Long.Max_Value - 起始时间]。

Salting(加盐)

在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。

 
A_foo0003
B_foo0001
C_foo0004
D_foo0002
E_foo0001
加盐的一个缺点:同一个key的数据可能不一样;
 

 

场景题

电信需求:用户(手机号)通过指定时间{年[月日]}查询通话详情!

12312341234 19878967896 2023-12-21 12:12:12 235

1) 预分区(散列性)

  • 分区键:00| 01| 02| ...  
  • 评估数据量,保证未来单个Region不能超过10G,同时考虑机器台数

2) 分区号

  • 00_ 01_ 02_ ...
  • 轮询: 散列性最好,不好查
  • 考虑查询条件:手机号 年[月日]
  • (手机号+年月日).hash() % 分区数:
  • (手机号+年月).hash() % 分区数: √  
  • (手机号+年).hash() % 分区数
  • (手机号).hash() % 分区数


3) 拼接字段(唯一性)

  • 考虑查询条件
  • 0X_12312341234_2023-12-12 12:12:12
  • 0X_2023-12-12 12:12:12_12312341234

4) 校验

  • 12312341234 2023-12
  • 0X_12312341234_2023-12-12 12:12:12
    • start:0X_12312341234_2023-12
    • stop: 0X_12312341234_2023-13
  • 0X_12312341234_2024-01
  • 0X_12312341234_2023-12|
  • 0X_2023-12-12 12:12:12_12312341234
    • start:0X_2023-12-00 00:00:00_12312341234
      • 0X_2023-12-21 12:12:12_12312341238
    • stop: 0X_2023-13-00 00:00:00_12312341234

项目中的RowKey是如何设计?

  • 需求:主流根据维度表的主键查询单条数据明细
  • 预分区
  • 使用MySQL维表的主键作为RowKey
  • 如果为预分区表,需要将主键Hash取分区号拼接原先的主键作为RowKey

 

内存优化

HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。conf/env.sh; export: HBASE_HEAPSIZE=1G

基础优化

1.允许在HDFS的文件中追加内容

hdfs-site.xml、hbase-site.xml

属性:dfs.support.append

解释:开启HDFS追加同步,可以优秀的配合HBase的数据同步和持久化。默认值为true。

2.优化DataNode允许的最大文件打开数

hdfs-site.xml


属性:dfs.datanode.max.transfer.threads

解释:HBase一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为4096或者更高。默认值:4096

 

3.优化延迟高的数据操作的等待时间

hdfs-site.xml

属性:dfs.image.transfer.timeout

解释:Fsimage文件传输超时时间,建议把该值设置为更大的值(默认60000毫秒),以确保Fsimage传输不会被timeout掉。

4.优化数据的写入效率

mapred-site.xml

属性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为true,第二个属性值修改为:org.apache.hadoop.io.compress.GzipCodec或者其他压缩方式。

 

5.设置RPC监听数量
hbase-site.xml
属性:hbase.regionserver.handler.count
解释:默认值为30,是regionserver上用于等待响应用户请求的线程数,读写请求较多时,增加此值。

6.优化HStore文件大小
hbase-site.xml
属性:hbase.hregion.max.filesize
解释:该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。

7.优化hbase客户端缓存
hbase-site.xml
属性:hbase.client.write.buffer
解释:用于指定HBase客户端写缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。

8.指定scan.next扫描HBase所获取的行数
hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于设置HBase scanner一次从服务端抓取的数据条数,默认情况下一次一条。通过将其设置成一个合理的值,可以减少scan过程中next()的时间开销,但是需要通过客户端的内存来缓存这些记录。值越大,消耗内存越大。

9.flush、compact、split机制
当MemStore达到阈值,将Memstore中的数据Flush进Storefile;compact机制则是把flush出来的小文件合并成大的Storefile文件。split则是当Region达到阈值,会把过大的Region一分为二。
涉及属性:
即:128M就是Memstore的默认阈值
hbase.hregion.memstore.flush.size:134217728
即:这个参数的作用是当单个HRegion内某一Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模型来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。

hbase.regionserver.global.memstore.upperLimit:0.4
hbase.regionserver.global.memstore.lowerLimit:0.38
即:当MemStore使用内存总量达到hbase.regionserver.global.memstore.upperLimit指定值时,将会有多个MemStores flush到文件中,MemStore flush 顺序是按照大小降序执行的,直到刷新到MemStore使用内存略小于lowerLimit

 

 
posted @ 2019-10-10 10:54  kris12  阅读(389)  评论(0编辑  收藏  举报
levels of contents