HBase(十)架构和内部实现

网友投稿 502 2022-05-28

Hbase继承BigTable,采用LSM树(Log-Structured Merge Tree)结构存储,与RDBMS常用的B+trees不同。

B+trees能通过主键对记录进行高效插入、查询、删除。是一个动态、多层、有上下界的索引。分段B+trees效果好于二叉树的数据划分。B+trees因为叶子节点相互连接且按主键有序,所以能提供范围扫描功能,避免了遍历操作。B+trees是顺序的把表重写,表的范围查询是磁盘的多段连续读取。B+trees每次查找需要访问磁盘log(N)次,所以主要受限于磁盘寻道速度。

LSM树组织数据方式不同,输入数据首先被存储在日志文件中,文件中的数据完全有序。日志被修改时,对应的更新会先保存在内存中来加速查询。内存在多次数据修改后逐渐沾满,LSM树会把有序的“键-数据记录”写入磁盘,并创建一个新的数据存储文件,持久化后,内存中数据可以清空。所有节点都是满的并按页存储,修改数据文件的操作通过滚动合并完成,即现有的页与内存flush的数据混合在一起管理,直到填满容量。多次数据刷写之后会创建许多数据存储文件,后台会自动将大量小文件聚合成大文件。查询数据时,首先查找内存,然后查找磁盘。数据删除只是会建一个标记,查找会跳过标记的数据,页重写时,这些删除标记的键才会被删除。

LSM和B+对比看,没有太多修改时,B+树表现的很好,因为这些修改要求执行高代价的优化操作以保证查询能在有限时间内完成。在随机位置添加的数据越多,速度越快,这些页成为碎片的速度就越快,用户写入速度可能比优化后重写文件的处理速度越快。更新和删除都需要磁盘寻道,所以就会限制于磁盘速度。LSM树利用内容和日志文件把随机写变成顺序写,读写独立,这两种操作间没有冲突。存储的数据布局较优,查询一个键所需要的磁盘寻道次数在一个可预测的范围内,且读取与该键连续的任意数量的记录都不会引发任何额外的磁盘寻道。基于LSM树的系统强调的是成本透明:假设有5个存储文件,一个访问最多需要5个磁盘寻道。B+树结构的RDBMS,没办法确定一次查询需要的磁盘寻道次数。

Hbase底层文件存储在HDFS中,实际的数据文件可能被HDFS分割成小块。Client查找行健是通过zookeeper完成,先查找ZK中-ROOT-的region server,从-ROOT-查找.META.表,从META表获取行健相关的region server。这个过程会缓存,下载client查找不用再次查找zk。

Client发起写请求时,请求会转交对应的HRegion实例处理。首先决定是否写入HLog类实现的WAL中。WAL是Hadoop SequenceFile,存储了HLogKey实例,键包括序列号和实际数据,服务器崩溃时可以恢复还没有持久化的数据。数据写入WAL后,数据会放到MemStore中,同时检查MemStore是否写满,满了就会刷写到磁盘中,flush由另一个HRegionServer线程处理。会写成一个新的HFile,同时保存最后写入的序列号,系统知道持久化到什么位置了。

Hbase在HDFS中的根目录可配置,默认是/hbase,可以配置不同的根目录,使不同的Hbase实例共享HDFS,可以用hadoop dfs –lsr来查看Hbase目录结构。

pmapp:~> hadoop dfs -lsr /hbase | head -100

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 12:06 /hbase/-ROOT-

-rw-r--r-- 3 acrosspm supergroup 728 2015-11-26 19:18 /hbase/-ROOT-/.tableinfo.0000000001

drwxr-xr-x - acrosspm supergroup 0 2015-11-26 19:18 /hbase/-ROOT-/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 16:57 /hbase/-ROOT-/70236052

drwxr-xr-x - acrosspm supergroup 0 2015-11-26 19:18 /hbase/-ROOT-/70236052/.oldlogs

-rw-r--r-- 3 acrosspm supergroup 421 2015-11-26 19:18 /hbase/-ROOT-/70236052/.oldlogs/hlog.1448536690090

-rw-r--r-- 3 acrosspm supergroup 109 2015-11-26 19:18 /hbase/-ROOT-/70236052/.regioninfo

drwxr-xr-x - acrosspm supergroup 0 2015-12-27 19:43 /hbase/-ROOT-/70236052/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-27 19:43 /hbase/-ROOT-/70236052/info

-rw-r--r-- 3 acrosspm supergroup 1486 2015-12-27 19:43 /hbase/-ROOT-/70236052/info/9b55c4386f4740e4ab8ae223e59a3b1b

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 12:06 /hbase/.META.

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 16:57 /hbase/.META./1028785192

drwxr-xr-x - acrosspm supergroup 0 2015-11-26 19:18 /hbase/.META./1028785192/.oldlogs

-rw-r--r-- 3 acrosspm supergroup 134 2015-11-26 19:18 /hbase/.META./1028785192/.oldlogs/hlog.1448536690294

-rw-r--r-- 3 acrosspm supergroup 111 2015-11-26 19:18 /hbase/.META./1028785192/.regioninfo

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 13:15 /hbase/.META./1028785192/.tmp

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 13:15 /hbase/.META./1028785192/info

-rw-r--r-- 3 acrosspm supergroup 1235 2016-01-04 13:15 /hbase/.META./1028785192/info/0994f57412b54ada8f9408ebdc454e10

-rw-r--r-- 3 acrosspm supergroup 318056 2016-01-04 06:00 /hbase/.META./1028785192/info/5fdb37189fe04f3999c2dcd69e7b676a

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 11:05 /hbase/.META./1028785192/recovered.edits

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:43 /hbase/.archive

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:43 /hbase/.archive/H_20160103_0000

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:43 /hbase/.archive/H_20160103_0000/b9cc9e6e5b4cd2d2c01e3090ed300340

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:43 /hbase/.archive/H_20160103_0000/b9cc9e6e5b4cd2d2c01e3090ed300340/family0

-rw-r--r-- 3 acrosspm supergroup 1324 2016-01-04 15:43 /hbase/.archive/H_20160103_0000/b9cc9e6e5b4cd2d2c01e3090ed300340/family0/b5c3a8cf77e94e2badb298d807d29639

drwxr-xr-x - acrosspm supergroup 0 2015-12-14 09:49 /hbase/.corrupt

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 15:56 /hbase/.logs

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 15:56 /hbase/.logs/11.12.13.253,15241,1451116615973

-rw-r--r-- 3 acrosspm supergroup 0 2015-12-26 15:56 /hbase/.logs/11.12.13.253,15241,1451116615973/11.12.13.253%2C15241%2C1451116615973.1451116616553

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 14:59 /hbase/.logs/pmeam1,15241,1451116615669

-rw-r--r-- 3 acrosspm supergroup 162986 2016-01-04 13:59 /hbase/.logs/pmeam1,15241,1451116615669/pmeam1%2C15241%2C1451116615669.1451887185552

-rw-r--r-- 3 acrosspm supergroup 0 2016-01-04 14:59 /hbase/.logs/pmeam1,15241,1451116615669/pmeam1%2C15241%2C1451116615669.1451890785896

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 14:57 /hbase/.logs/pmweb,15241,1451116616005

-rw-r--r-- 3 acrosspm supergroup 1380486 2016-01-04 13:57 /hbase/.logs/pmweb,15241,1451116616005/pmweb%2C15241%2C1451116616005.1451887044332

-rw-r--r-- 3 acrosspm supergroup 0 2016-01-04 14:57 /hbase/.logs/pmweb,15241,1451116616005/pmweb%2C15241%2C1451116616005.1451890644505

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:07 /hbase/.oldlogs

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 12:15 /hbase/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-11 11:17 /hbase/12H_20151203_0000

-rw-r--r-- 3 acrosspm supergroup 697 2015-12-03 12:15 /hbase/12H_20151203_0000/.tableinfo.0000000001

drwxr-xr-x - acrosspm supergroup 0 2015-12-03 12:15 /hbase/12H_20151203_0000/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-11 11:17 /hbase/12H_20151203_0000/e865997cef7a7975e7bc201804a0bde6

-rw-r--r-- 3 acrosspm supergroup 258 2015-12-03 12:15 /hbase/12H_20151203_0000/e865997cef7a7975e7bc201804a0bde6/.regioninfo

drwxr-xr-x - acrosspm supergroup 0 2015-12-04 17:43 /hbase/12H_20151203_0000/e865997cef7a7975e7bc201804a0bde6/family0

-rw-r--r-- 3 acrosspm supergroup 393668 2015-12-04 17:43 /hbase/12H_20151203_0000/e865997cef7a7975e7bc201804a0bde6/family0/321934dda7704c8ebe20f6aa80eb67f6

drwxr-xr-x - acrosspm supergroup 0 2015-12-11 11:17 /hbase/12H_20151204_0000

-rw-r--r-- 3 acrosspm supergroup 697 2015-12-04 12:15 /hbase/12H_20151204_0000/.tableinfo.0000000001

drwxr-xr-x - acrosspm supergroup 0 2015-12-04 12:15 /hbase/12H_20151204_0000/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-11 11:17 /hbase/12H_20151204_0000/b983a372d5eebc449cfcf89fb0c49070

-rw-r--r-- 3 acrosspm supergroup 258 2015-12-04 12:15 /hbase/12H_20151204_0000/b983a372d5eebc449cfcf89fb0c49070/.regioninfo

drwxr-xr-x - acrosspm supergroup 0 2015-12-05 18:43 /hbase/12H_20151204_0000/b983a372d5eebc449cfcf89fb0c49070/family0

-rw-r--r-- 3 acrosspm supergroup 413433 2015-12-05 18:43 /hbase/12H_20151204_0000/b983a372d5eebc449cfcf89fb0c49070/family0/f8009c70f33b41a8a44d1ad3a7f2efab

……

可以看到文件有两类,一类在根目录下,一类在表目录下。

pmapp:~> hadoop dfs -ls /hbase/

Found 347 items

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 12:06 /hbase/-ROOT-

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 12:06 /hbase/.META.

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 16:00 /hbase/.archive

drwxr-xr-x - acrosspm supergroup 0 2015-12-14 09:49 /hbase/.corrupt

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 15:56 /hbase/.logs

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 15:59 /hbase/.oldlogs

drwxr-xr-x - acrosspm supergroup 0 2016-01-04 12:15 /hbase/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-04 14:57 /hbase/WW_20151123_0000

drwxr-xr-x - acrosspm supergroup 0 2015-12-11 11:17 /hbase/WW_20151130_0000

……

.logs

目录下存着WAL文件,对每个HRegionServer,日志目录都包含一个对应的子目录,每个子目录下有多个HLog文件(滚动日志),一个region server的所有region共享一组HLog文件。

HDFS使用内置的append机制来追加写入文件,所以只有等文件大小达到一个完整的块时,文件对用户才是可见的,包括hadoopdfs –lsr命令,所以最新创建的log文件大小是0。日志滚动后,日志文件被关闭,HDFS才列出其正确的大小,下一个日志大小又从0开始了。滚动默认是1小时,通过hbase.regionserver.logroll.period配置。

所有修改都持久化到存储文件后,日志文件会反倒根目录下的.oldlogs目录。在hbase.master.logcleaner.ttl属性时间出发后(默认10分钟),旧日志文件会被master删除。master默认每分钟会检查一下这些文件,间隔是通过hbase.master.cleaner.interval配置的。

-rw-r--r-- 3 acrosspm supergroup 38 2015-11-26 19:18 /hbase/hbase.id

-rw-r--r-- 3 acrosspm supergroup 3 2015-11-26 19:18 /hbase/hbase.version

这两个文件存储集群唯一ID和文件格式的版本信息

splitlog和corrupt

存储日志拆分过程中产生的中间拆分文件和损坏的文件

Hbase的每张表都有自己的目录,目录位于根目录,每张表都包含一个.tableinfo的顶层文件,该文件存储表对应序列化后的HTableDescriptor,包括了表和列族的定义,内容可以被读取。

.tmp目录包含一些临时数据,如当心的.tableinfo文件生成时的临时数据就存在这个目录

每个表目录里面,表模式中每个列族都有一个单独的目录,目录名字的一部分是region名字的MD5值,是编码region名得到的。

pmapp:~> hadoop dfs -lsr /hbase/W_20151026_0000

-rw-r--r-- 3 acrosspm supergroup 693 2015-12-15 11:24 /hbase/W_20151026_0000/.tableinfo.0000000001

drwxr-xr-x - acrosspm supergroup 0 2015-12-15 11:24 /hbase/W_20151026_0000/.tmp

drwxr-xr-x - acrosspm supergroup 0 2015-12-26 11:11 /hbase/W_20151026_0000/9740e64a90d75d13a85b83c470a69fd3

-rw-r--r-- 3 acrosspm supergroup 252 2015-12-15 11:24 /hbase/W_20151026_0000/9740e64a90d75d13a85b83c470a69fd3/.regioninfo

drwxr-xr-x - acrosspm supergroup 0 2015-12-18 11:50 /hbase/W_20151026_0000/9740e64a90d75d13a85b83c470a69fd3/family0

-rw-r--r-- 3 acrosspm supergroup 3671703 2015-12-18 11:50 /hbase/W_20151026_0000/9740e64a90d75d13a85b83c470a69fd3/family0/3838fd70dcd3411f8b5437cfc1abc356

MD5散列是为了满足文件系统的命名规则,避免任意的特殊符号。region文件的总体结构是://///

.regioninfo文件

包含了对应region 的HRegionInfo实例序列化后的信息,与.tableinfo文件类似,都可以通过外部工具查看region的相关信息,如hbase的hbck工具就是用它检查并生成元数据表中丢失的条目。

.tmp目录是按需创建的,存放临时文件,如一次合并的重写文件,如果这个过程完成,这些临时生成的文件通常会移到region目录中。

WAL回放时,任何未提交的修改都会写入每个region的一个单独的文件中,如果日志拆分已经完成,这些文件会被自动移到临时的recovered.edits目录中。当region被打开时,region server会看到需要恢复的文件,并回放其中响应的条目。WAL拆分和region 的拆分有明显的区别,有时很难区分文件系统中的文件名和目录名,因为他们名称中都有拆分的字样。

所以,如果region目录中没有.tmp目录,就说明这个region还没有被压缩过;如果没有recovered.edits目录,就以为这WAL还没有进行过回放。

一个region超过配置的阈值(hbase.hregion.max.filesize),region就会拆分,region server在父目录中创建splits目录,临时存放两个子region 的相关的数据,接下来会关闭这个region,不再接受新的请求。在目录下会使用多线程创建region必要的目录结构和文件。如果拆分成功,会移到表目录中,形成两个新的region,每region 是原来region的一半。.META.表中父region的状态会被更新,表示现在拆分的节点和子节点是什么。以上过程可以避免父region被重新意外打开。

两个子region都准备好后,会被同一个服务器并行打开,打开的过程包括更新.META.表,这两个region上线并接受请求。初始化两个region后会对region内容进行合并操作,在替换引用文件前会把父region的存储文件异步重写到两个子region中,以上过程会在子region 的.tmp目录中执行。一旦生成了重写之后的文件,会自动取代引用文件。

最后,父region会被clear,即在.META.表汇总删除,在磁盘上的所有文件都被删除。最后通知master拆分,然后可能由于负载均衡把新的region移动到其他的region server上。

整个拆分的过程中,所有的步骤都在zookeeper中进行跟踪,所以一个服务器宕机后,其他进程也可以知道这个region 的状态。

memstore不断刷写,会生成很多磁盘文件,当数目达到阈值,合并操作就会触发,把它们合并成若干个大文件。这个过程会持续到文件体积达到最大存储文件大小,触发一个region拆分。

合并分两种,minor和major。

minor合并负责重写最后生成的几个文件到一个更大的文件中,文件数量是hbase.hstore.compaction.min配置的,默认=3,且最小值需要>=2,过大的数字会延迟minor操作,带来更大的压缩资源消耗和时间。最大可以处理的文件数量默认是10,通过hbase.hstore.compaction.max配置。hbase.hstore.compaction.min.size(默认为memstore刷写大小)和hbase.hstore.compaction.max.size(默认为Long.MAX_VALUE)配置项可以过滤需要合并的文件列表,任何超过这个范围大小的文件都会排除在外。其中最小文件大小,是一个阈值,而不是针对每个文件的限制,小于这个阈值的文件也可能会包含在内。 使用hbase.hstore.compaction.ratio(默认是1.2,即120%)配置,确保在选择过程中有足够的文件,评估是按老到新的顺序进行,保证旧文件会先被合并。

Major合并,是把所有的文件压缩成一个单独的文件,在压缩检查时,系统会判断执行那种合并。在memstore被flush到磁盘后会触发这个检查,或在shell中compact和major compact命令手动触发检查,或者是java api调用触发等等,region server会调用CompactionChecker类的实现,以一个固定的周期触发检查,这个周期由hbase.server.thread.wakefrequency控制。注意这个周期要乘以1000,由hbase.server.thread.wakefrequency.multiplier参数配置,避免任务执行太频繁。

服务器会先检查上次运行到现在是否达到hbase.hregion.majorcompaction,默认为24小时的配置,如果没达到major合并周期的时间,系统就会执行minor合并。hbase通过参数hbase.hregion.majorcompaction.jitter(默认为0.2,即20%)进行时间周期的抖动,避免所有region在统一时间进行major合并。

当minor合并包括所有的存储文件,且所有文件均未达到设置的每次压缩的最大文件数时,minor合并可能被提升成major合并。 和compaction的主要区别是major-compaction会回收系统删除的数据和TTL到期的数据。

Hbase存储数据是基于HFile的,而HFile是基于Hadoop的TFile类,并参考google的BigTable架构使用SSTable格式。

文件长度是可变的,唯一固定的块是File info和Trailer。Trailer块有指向其他块的指针。它是在持久化数据到文件结束时写入的,写入后即确定为不可变的数据存储文件。Index块记录Data和Meta块的偏移量。Date和Meta块实际上是可选的,但是Data块实际上一般是有的。

块大小由HColumnDescriptor配置,默认是64KB,一般应用推荐在8K到1M之间,块越小,随机访问越好,但是需要更多内存来存储块索引,创建过程也会变慢。块越大,越利于顺序访问。

每个块都包含一个Magic头部和一定数量的序列化的KeyValue实例,如果没有使用压缩,每个块大小和配置的块大小差不多,但是如果存储的KeyValue大于块大小配置,也会写入。

Hbase块和Hadoop块不相关,HDFS不知道Hbase存储的是什么,看到的其实是二进制文件。

Hbase提供了一个直接访问HFile的工具:

pmapp:~> hbase org.apache.hadoop.hbase.io.hfile.HFile

16/01/05 15:28:47 INFO util.ChecksumType: Checksum using org.apache.hadoop.util.PureJavaCrc32

usage: HFile [-a] [-b] [-e] [-f

] [-k] [-m] [-p] [-r

] [-s] [-v]

[-w

]

-a,--checkfamily Enable family check

-b,--printblocks Print block index meta data

-e,--printkey Print keys

-f,--file

File to scan. Pass full-path; e.g.

hdfs://a:9000/hbase/.META./12/34

-k,--checkrow Enable row order check; looks for out-of-order

keys

-m,--printmeta Print meta data of file

-p,--printkv Print key/value pairs

-r,--region

Region to scan. Pass region name; e.g. '.META.,,1'

-s,--stats Print statistics

HBase(十)架构和内部实现

-v,--verbose Verbose output; emits file and meta data

delimiters

-w,--seekToRow

Seek to this row and print all the kvs for this

row only

pmapp:~> hbase org.apache.hadoop.hbase.io.hfile.HFile -v -m -p -f /hbase/12H_20151210_0000/e045ac641e54fa46c1746305bc410e0e/family0/c1cfc527eabc431eb1179802f5cc0e62

16/01/05 15:32:03 INFO util.ChecksumType: Checksum using org.apache.hadoop.util.PureJavaCrc32

Scanning -> /hbase/12H_20151210_0000/e045ac641e54fa46c1746305bc410e0e/family0/c1cfc527eabc431eb1179802f5cc0e62

16/01/05 15:32:04 INFO hfile.CacheConfig: Allocating LruBlockCache with maximum size 733.4m

16/01/05 15:32:04 ERROR metrics.SchemaMetrics: Inconsistent configuration. Previous configuration for using table name in metrics: true, new configuration: false

K: 12H_20151210_0000_1449720912058111912913914725632/family0:column0/1449721233852/Put/vlen=1750/ts=0 V: PK\x03\x04\x14\x00\x08\x08\x08\x00\x90b\x8AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\……

Block index size as per heapsize: 1208

reader=/hbase/12H_20151210_0000/e045ac641e54fa46c1746305bc410e0e/family0/c1cfc527eabc431eb1179802f5cc0e62,

compression=none,

cacheConf=CacheConfig:enabled [cacheDataOnRead=true] [cacheDataOnWrite=false] [cacheIndexesOnWrite=false] [cacheBloomsOnWrite=false] [cacheEvictOnClose=false] [cacheCompressed=false],

firstKey=12H_20151210_0000_1449720912058111912913914725632/family0:column0/1449721233852/Put,

lastKey=12H_20151210_0000_145040919421611191291392315189/family0:column0/1451446709688/Put,

avgKeyLen=74,

avgValueLen=2598,

entries=153,

length=411583

Trailer:

fileinfoOffset=411028,

loadOnOpenDataOffset=410364,

dataIndexCount=7,

metaIndexCount=0,

totalUncomressedBytes=410943,

entryCount=153,

compressionCodec=NONE,

uncompressedDataIndexSize=616,

numDataIndexLevels=1,

firstDataBlockOffset=0,

lastDataBlockOffset=409477,

comparatorClassName=org.apache.hadoop.hbase.KeyValue$KeyComparator,

majorVersion=2,

minorVersion=0

Fileinfo:

DATA_BLOCK_ENCODING = NONE

DELETE_FAMILY_COUNT = \x00\x00\x00\x00\x00\x00\x00\x00

EARLIEST_PUT_TS = \x00\x00\x01Q\x8A\x19\xAAb

MAJOR_COMPACTION_KEY = \xFF

MAX_SEQ_ID_KEY = 1263241

TIMERANGE = 1449720916578....1451446709688

hfile.AVG_KEY_LEN = 74

hfile.AVG_VALUE_LEN = 2598

hfile.LASTKEY = \x00012H_20151210_0000_145040919421611191291392315189\x07family0column0\x00\x00\x01Q\xF0\xF7-\xB8\x04

Mid-key: \x00112H_20151210_0000_1449720924811111912913914725632\x07family0column0\x00\x00\x01Q\x8A\x1E\x98\x0B\x04

Bloom filter:

Not present

Delete Family Bloom filter:

Not present

Scanned kv count -> 153

HFile中每个KeyValue其实是一个低级的字节数组,允许零复制访问数据。

键中包含了指定单元的全维度内容。包含了行健、列族名、列限定符等。所以key在value小的时候会造成较大的负担,如果使用的值比较短,尽量保持一个比较短的键。另外,压缩也可以缓解这个问题。

Hbase的HLog相当于一个regionserver的日志中心,所有region共享。这部分涉及是参考BigTable论文的,如果每个region一个WAL,会产生大量文件,底层文件系统实现起来会有大量的磁盘寻道,性能会降低。

数据请求RPC到达服务端后,先写入HLOG,再写入MemStore。

HRegion实例化时,HLog实例会作为构造函数的参数传入。HLog核心功能是append()方法。Put、Delete等功能可以设置是否写WAL,除非大批量导入的场景,一般还是要开启WAL的。HLog通过一个AtomicLong的序列号,保证顺序写入。

WAL是Hadoop的SquenceFile格式,键值对中,值是client发送的修改请求,封装为WALEdit类,键是HLogKey实例,KeyValue的归属,如region和表名,都存在这个类中,也会包含上面说的序列号和写入时间。

WAL的异步写入控制是LogSyncer类,这个类实例会单独运行在一个实例中。

WAL日志滚动控制为LogRoller类,可以配置hbase.regionserver.logroll.period参数控制滚动间隔,默认是1个小时。hbase.regionserver.logroll.multiplier参数控制达到块大小的百分比发生滚动,默认是0.95。及hbase.regionserver.hlog.blocksize参数控制块大小。

HLog.cleanOldLogs()会定期检查存储文件和WAL中的序列号,定期清理WAL。

由于WAL被所有region共享,所以,数据恢复会分成两个步骤,日志拆分和数据恢复执行。

拆分由ServerShutdownHandler类完成,就是把共享的log拆分为每个region一个log。hbase.master.distributed.log.splitting参数控制是否启用分布式拆分,在单点拆分下,是多线程处理的,线程数默认是3,由hbase.regionserver.hlog.splitlog.writer.threads参数控制。

拆分首先把数据改动写入根目录的splitlog文件夹,包含的元信息写在路径和文件名中。

不能解析的log会放到.corrupt文件夹,可以设置是否静默异常,如果非静默,抛出异常整个过程就会停止。

拆分最后,每个region对应的文件会移到对应的region目录。

region打开后先检查recovered.edits目录,如果存在就会打开其中的文件,读取变更记录。其中记录的序号小于等于存储文件中的序号,都会被抛弃。整个目录所有文件都处理完,整个目录就会删除。

如果中间遇到问题,还是同样静默配置的原理,要么终止过程,要么其文件会被修改后缀名供人工检查。

灾备场景下,也是通过WAL的方式同步数据。日志同步,数据异步,通过zk跟踪。默认机制挑选10%的机器进行复制,如集群100台,有1台机器负责同步。

Hbase内部是三层B+tree的索引方式,ROOT region 能映射2:6 [1] 105 个.META. region,

依次能映射总的6:9x1010 个user region,意味着大约是1:8x1019(264) 字节用户数据(即214PB)。

client 会将查询过的位置信息保存缓存起来,缓存不会主动失效,因此如果client 上的缓存全部失效,则需要进行6 次网络来回,才能定位到正确的region(其中三次用来发现缓存失效,另外三次用来获取位置信息)。

Hbase内部的数据查询由QueryMatcher和ColumnTracker来实现,一个是精确匹配列,一个是匹配所有列。

匹配可以使用布隆过滤器和时间戳的方式先过滤大部分数据。

Scan操作通过RegionScanner实现,该类为每个store(每个store是一个列族)配一个StoreScanner,会扫描MemStore和存储文件。StoreScanner通过QueryMatcher描述具体哪个KeyValue要包含进来。

RegionScanner内部用KeyValueHeap类,按时间戳排序来处理存储扫描器,StoreScanner也同样是复用这个类对存储排序。

get的内部实现,实际上就是个单步scan。

region状态变更由Master跟踪,使用AssignmentManager类管理。

状态列表如下:

Offline The region is offline.

Pending Open A request to open the region was sent to the server.

Opening The server has started opening the region.

Open The region is open and fully operational.

Pending Close A request to close the region has been sent to the server.

Closing The server is in the process of closing the region.

Closed The region is closed.

Splitting The server started splitting the region.

Split The region has been split by the server.

zk中根目录由zookeeper.znode.parent参数配置,默认为/hbase。

[zk: pmapp:15248(CONNECTED) 1] ls /hbase

[splitlog, online-snapshot, unassigned, backup-masters, root-region-server, rs, table92, draining, table, hbaseid, shutdown]

/hbase/hbaseid

与hbase.id文件内容相同

/hbase/master

master服务器

/hbase/replication

副本信息

/hbase/root-region-server

-ROOT- region所在的region server机器名

/hbase/rs

所有region server的根节点,每个znode都是临时节点,且node名是region server名称

/hbase/shutdown

跟踪集群状态,包括集群启动时间、集群关闭

/hbase/splitlog

数据恢复的日志拆分步骤的相关信息的父节点

/hbase/table

表被禁用时,信息回添加到这个node,表名是新建的znode名,content是DISABLED。

/hbase/unassigned

AssignmentManager类用来跟踪region生命周期,包含not open的region,znode名是region的hash。

转载请注明出处:华为云博客 https://portal.hwclouds.com/blogs

hbase

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:云中筑梦人:大凉山的AI课
下一篇:2020-07-17:线上一个服务有4个实例突然变得访问很慢,你会从什么地方入手找原因?
相关文章