Hbase原理| 优化
HBase的设计目标就是为了那些巨大的表,如数十亿行、数百万列。
面向列,准确的说是面向列族。每行数据列可以不同。
架构
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 是与关系数据库系统中的数据库类似的表的逻辑分组
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:
Mem Store
①默认128M
②region中有多个Mem Store,region中所有的MamStore达到128*4,触发整个region的flush
③memstore数据存储超过1h
flush刷写最小单位是region而不是MemStore;而判断标准是region
2)regionserver级别的flush :
regionserver,多个region
高点:memstore占了 0.4*java虚拟机的堆空间--->展开flush,并阻塞写操作;
会先flush占内存最大的memstore,刷到低点(jvm堆*0.4*0.95)就停止flush,不再阻塞写操作;
数据切分和合并过程
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个时将触发合并;拆的太散就合
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字节;数据是以kv形式存储在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的数据可能不一样;
内存优化
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