上图是HBase的存储架构图。
由上图可以知道,客户端是通过Zookeeper找到HMaster,然后再与具体的Hregionserver进行沟通读写数据的。
具体到物理实现,细节包括以下这些:
首先要清楚HBase在hdfs中的存储路径,以及各个目录的作用。在hbase-site.xml 文件中,配置项 <name>hbase.rootdir</name>默认 “/hbase”,就是hbase在hdfs中的存储根路径。以下是hbase0.96版本的个路径作用。1.0以后的版本请参考这里: https://blog.bcmeng.com/post/hbase-hdfs.html
1、 /hbase/.archive
HBase 在做 Split或者 compact *** 作完成之后,会将 HFile 移到.archive 目录中,然后将之前的 hfile 删除掉,该目录由 HMaster 上的一个定时任务定期去清理。
2、 /hbase/.corrupt
存储HBase损坏的日志文件,一般都是为空的。
3、 /hbase/.hbck
HBase 运维过程中偶尔蔽升会遇到元数据不一致的情况,这时候会用到提供的 hbck 工具去修复,修复过程中会使用该目录作为临时过度缓冲。
4、 /hbase/logs
HBase 是支持 WAL(Write Ahead Log) 的,HBase 会在第一次启动之初会给每一台 RegionServer 在.log 下创建一个目录,若客户端如果开启WAL 模式,会先将数据写入一份到.log 下,当 RegionServer crash 或者目录达到一定大小,会开启 replay 模式,类似 MySQL 的 binlog。
5、 /hbase/oldlogs
当.logs 文件夹中的 HLog 没用之后会 move 到.oldlogs 中,HMaster 会定期去清理。
6、 /hbase/.snapshot
hbase若开启了 snapshot 功能之后,对某一个用户表建立一个 snapshot 之后,snapshot 都存储在该目录下,如对表test 做了一个 名为sp_test 的snapshot,就会在/hbase/.snapshot/目录下创建一个sp_test 文件夹,snapshot 之后的所有写入都是记录在这个 snapshot 之上。
7、 /hbase/.tmp
当对表做创建或者删除 *** 作的时候,会将表move 到该 tmp 目录下,然后再去做处理 *** 作。
8、 /hbase/hbase.id
它是一个文件,存储集群唯一的 cluster id 号,是一个 uuid。
9、 /hbase/hbase.version
同样也是一宏拿老个文件,存储集群的版本号,貌似是加密的,看不到,只能通过web-ui 才能正确显示出来
10、 -ROOT-
该表是一张的HBase表,只是它存储的是.META.表的信息。通过HFile文件的解析脚本 hbase org.apache.hadoop.hbase.io.hfile.HFile -e -p -f 可以查看其存储的内容,如下所示:
以上可以看出,-ROOT-表记录的.META.表的所在机器是dchbase2,与web界面看到的一致:
11、 .META.
通过以上表能找到.META.表的信息,该表也是一张hbase表,通过以上命令,解析其中一个region:
以敏神上可以看出,adt_app_channel表的数据记录在dchbase3这台reginserver上,也与界面一致,如果有多个region,则会在表名后面加上rowkey的范围:
通过以上描述,只要找到-ROOT-表的信息,就能根据rowkey找到对应的数据,那-ROOT-在哪里找呢?从本文一开始的图中可以知道,就是在zookeeper中找的。进入zookeeper命令行界面:
可以看出-ROOT-表存储在 dchbase3 机器中,对应界面如下:
以上就是HBase客户端根据指定的rowkey从zookeeper开始找到对应的数据的过程。
那在Region下HBase是如何存储数据的呢?
以下就具体 *** 作一张表,查询对应的HFile文件,看HBase的数据存储过程。
在HBase创建一张表 test7,并插入一些数据,如下命令:
查看wal日志,通过 hbase org.apache.hadoop.hbase.regionserver.wal.HLog --dump -p 命令可以解析HLog文件,内容如下:
查看HFile文件,内容如下:
由此可见,HFile文件就是存储HBase的KV对,其中Key的各个字段包含了的信息如下:
由于hbase把cf和column都存储在HFile中,所以在设计的时候,这两个字段应该尽量短,以减少存储空间。
但删除一条记录的时候,HBase会怎么 *** 作呢?执行以下命令:
删除了rowkey为200的记录,查看hdfs,原来的HFile并没有改变,而是生成了一个新的HFile,内容如下:
所以在HBase中,删除一条记录并不是修改HFile里面的内容,而是写新的文件,待HBase做合并的时候,把这些文件合并成一个HFile,用时间比较新的文件覆盖旧的文件。HBase这样做的根本原因是,HDFS不支持修改文件。
MasterFileSystem.splitLog
hregion 的LOGDIR
hdfs上的region Hlog
如果任务没有重复发布过,将 task entry 发布到 SplitLogManagerCoordinatio(ZKSplitLogManagerCoordination)
/如果像master 报告成功,做初始化. 方法内调用startServiceThreads()
其中包括 SplitLogWorker
在/hbase/splitlog zknode上等待任务可用。一次只处理一个任务。这
策略对在集群答答中可能发生同时日志拆分数量设置了上限在
尝试获取任务zk节点上的“锁”,以拥有并执行任务。
1.1 假设Master当前发布了4个任务,即当前需要回放4个日志文件,分别为hlog1、hlog2、hlog3和hlog4
1.2 RegionServer1抢占到了hlog1和hlog2日志,RegionServer2抢占到了hlog3日志,RegionServer3抢占到了hlog4日志
1.3 以RegionServer1为例,其抢占到hlog1和hlog2日志之后会分别分发给两个HLogSplitter线程进行处理,HLogSplitter负责对日志文件执行具体的切分,切分思路还是首先读出日志中每一个<HLogKey, WALEdit>数据对,根据HLogKey所属Region写入不同的Region Buffer
1.4 每个Region Buffer都会有一个对应的写线程,将buffer中的日志数据写入hdfs中,写入路径为/hbase/table/region2/seqenceid.temp,其中seqenceid是一个日志中某个region对应的最大sequenceid
LogRecoveredEditsOutputSink的工作是戚汪直接按照region,把相对应的log写到hdfs的 hbase.rootdir/data/namespace(比如test)/table_name/region_encoded_name/recovered.edits下。后续region被其他region server open时,会来这看是不是有需要回放的hlog.
1.5 后续region被其他region server open时,针对某一region回放日志只需要将该region对应的所有文件按照sequenceid由小到大依次进行回放即可
这种Distributed Log Splitting方式可以很大程度上加快整个故障恢复的进程,正常故障恢复时间可以降低到分钟级别。然而,这种方式会产生很多日志小文件,产生的文件数将会是M * N,其中M是待切分的总hlog数量,N是一个宕机RegionServer上的Region个数。假如一个RegionServer上有200个Region,并且有90个hlog日志,一旦该RegionServer宕机,那这种方式的恢复过程将会创建 90 * 200 = 18000个小文件。这还只是一个RegionServer宕机的情况,如果是整个集群宕机小文件将会更多!!!
znode : splitWAL
znode :recovering-regions
在region打开的时候,我们从HRegionServer的openRegion方法一路跟踪,中间历经OpenMetaHandler,再到HRegion.openHRegion方法,终于在initializeRegionStores方法里面找到了那么一句话。
打开region
HRegion.initialize
HRegion.initializeRegionInternals
ServerRegionReplicaUtil.shouldReplayRecoveredEdits
读取recovered.edits下面的日高举仔志,符合条件的就加到MemStore里面去,完成之后,就把这些文件删掉。大家也看到了,这里通篇讲到一个logSeqNum,哪里都有它的身影,它实际上是FSHLog当中的一个递增的AtomicLong,每当往FSLog里面写入一条日志的时候,它都会加一,然后MemStore请求flush的时候,会调用FSLog的startCacheFlush方法,获取(logSeqNum+1)回来,然后写入到StoreFile的sequenceid字段,再次拿出来的时候,就遍历这个HStore下面的StoreFile的logSeqNum,取出来最大的跟它比较,小于它的都已经写过了,没必要再写了。
参考自: https://zhuanlan.zhihu.com/p/27885715
https://blog.csdn.net/qq_26803795/article/details/79152808?utm_source=blogxgwz6
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)