【大数据】——Hbase总结

小咪咪 2022-04-25 09:24 326阅读 0赞

一、前言

  1. HBase Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。HBaseGoogle Bigtable的开源实现,类似Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统;Google运行MapReduce来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable利用 Chubby作为协同服务,HBase利用Zookeeper作为对应。Hbase介绍视频:[https://www.bilibili.com/video/av21629305?from=search&seid=6409533564158911048][https_www.bilibili.com_video_av21629305_from_search_seid_6409533564158911048];Hbase原理:[https://cloud.tencent.com/developer/article/1120649][https_cloud.tencent.com_developer_article_1120649]

二、Hbase原理

  1. Hadoop HDFSHBase提供了高可靠性的底层存储支持,Hadoop MapReduceHBase提供了高性能的计算能力,ZookeeperHBase提供了稳定服务和failover机制。PigHive还为HBase提供了高层语言支持,使得在HBase上进行数据统计处理变的非常简单。 Sqoop则为HBase提供了方便的RDBMS(关系型数据库)数据导入功能,使得[传统数据库][Link 1]数据向HBase中迁移变的非常方便。

1、什么是Hbase: Hbase是一个可以运行在Hadoop集群上的NoSQL数据库。
Hbase组件
• Hbase Master
• Region Server
• Region
• Zookeeper
Hbase的架构图如下图所示:

这里写图片描述

70 1

2、我们先看下 HBase 的写流程:

aHR0cDovL3N0YXRpYy5vc2NoaW5hLm5ldC91cGxvYWRzL3NwYWNlLzIwMTMvMTIyNC8xODA5MDBfWjBxUF81Njg4MTgucG5n

  1. 通常 MapReduce 在写HBase时使用的是 TableOutputFormat 方式,在reduce中直接生成put对象写入HBase,该方式在大数据量写入时效率低下(HBaseblock写入,频繁进行flushsplitcompact等大量IO操作),并对HBase节点的稳定性造成一定的影响(GC时间过长,响应变慢,导致节点超时退出,并引起一系列连锁反应),而HBase支持 bulk load 的入库方式,它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接在HDFS中生成持久化的HFile数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载,在大数据量写入时能极大的提高写入效率,并降低对HBase节点的写入压力。

通过使用先生成HFile,然后再BulkLoad到Hbase的方式来替代之前直接调用HTableOutputFormat的方法有如下的好处:
(1)消除了对HBase集群的插入压力
(2)提高了Job的运行速度,降低了Job的执行时间
目前此种方式仅仅适用于只有一个列族的情况,在新版 HBase 中,单列族的限制会消除。

3、bulkload 流程与实践

  1. bulkload 方式需要两个Job配合完成:

(1)第一个Job还是运行原来业务处理逻辑,处理的结果不直接调用HTableOutputFormat写入到HBase,而是先写入到HDFS上的一个中间目录下(如 middata)
(2)第二个Job以第一个Job的输出(middata)做为输入,然后将其格式化HBase的底层存储文件HFile
(3)调用BulkLoad将第二个Job生成的HFile导入到对应的HBase表中

aHR0cDovL3d3dy5hYm91dHl1bi5jb20vZGF0YS9hdHRhY2htZW50L2ZvcnVtLzIwMTQxMi8yOC8xMzM4NDRmOTNlc29hdGpkdzF5OHQ5LnBuZw

三、常用命令

  1. 说明:新版hbase取消了对HQL的支持,只能使用shell
  2. 命令:disable 'tableName' --disable表。注:修改表结构时,必须要先disable表。
  3. 命令:enable 'tableName' --使表可用
  4. 命令:drop 'tableName' --删除表

下面我们再看看看HBase的一些基本操作命令,我列出了几个常用的HBase Shell命令,如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ0OTMwMA_size_16_color_FFFFFF_t_70

# 登陆hbase

  1. hbase shell

# 浏览所有表

  1. hbase(main):001:0> list

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ0OTMwMA_size_16_color_FFFFFF_t_70 1

# 创建表

语法:create <table>, {NAME => <family>, VERSIONS => <VERSIONS>}
创建一个User表,并且有一个info列族

  1. hbase(main):002:0> create 'User','info'
  2. 0 row(s) in 1.5890 seconds
  3. => Hbase::Table - User

# 查看表详情

  1. hbase(main):004:0> describe 'OTTable'

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ0OTMwMA_size_16_color_FFFFFF_t_70 2

# 插入数据到表

  1. hbase(main) > put 'student','1001','info:name','Thomas'
  2. hbase(main) > put 'student','1001','info:sex','male'
  3. hbase(main) > put 'student','1001','info:age','18'
  4. hbase(main) > put 'student','1002','info:name','Janna'
  5. hbase(main) > put 'student','1002','info:sex','female'
  6. hbase(main) > put 'student','1002','info:age','20'

# 根据rowKey查询某个记录

语法:get <table>,<rowkey>,[<family:column>,....]

  1. hbase(main):008:0> get 'User', 'row2'
  2. COLUMN CELL
  3. info:age timestamp=1502368069926, value=18
  4. 1 row(s) in 0.0280 seconds
  5. hbase(main):028:0> get 'User', 'row3', 'info:sex'
  6. COLUMN CELL
  7. info:sex timestamp=1502368093636, value=man
  8. hbase(main):036:0> get 'User', 'row1', {COLUMN => 'info:name'}
  9. COLUMN CELL
  10. info:name timestamp=1502368030841, value=xiaoming
  11. 1 row(s) in 0.0120 seconds

# 查询所有记录

语法:scan <table>, {COLUMNS => [ <family:column>,.... ], LIMIT => num}
扫描所有记录

  1. hbase(main):009:0> scan 'User'
  2. ROW COLUMN+CELL
  3. row1 column=info:name, timestamp=1502368030841, value=xiaoming
  4. row2 column=info:age, timestamp=1502368069926, value=18
  5. row3 column=info:sex, timestamp=1502368093636, value=man
  6. 3 row(s) in 0.0380 seconds

# 扫描前2条

  1. hbase(main):037:0> scan 'User', {LIMIT => 2}
  2. ROW COLUMN+CELL
  3. row1 column=info:name, timestamp=1502368030841, value=xiaoming
  4. row2 column=info:age, timestamp=1502368069926, value=18
  5. 2 row(s) in 0.0170 seconds

# 范围查询

  1. hbase(main):011:0> scan 'User', {STARTROW => 'row2'}
  2. ROW COLUMN+CELL
  3. row2 column=info:age, timestamp=1502368069926, value=18
  4. row3 column=info:sex, timestamp=1502368093636, value=man
  5. 2 row(s) in 0.0170 seconds
  6. hbase(main):012:0> scan 'User', {STARTROW => 'row2', ENDROW => 'row2'}
  7. ROW COLUMN+CELL
  8. row2 column=info:age, timestamp=1502368069926, value=18
  9. 1 row(s) in 0.0110 seconds
  10. hbase(main):013:0> scan 'User', {STARTROW => 'row2', ENDROW => 'row3'}
  11. ROW COLUMN+CELL
  12. row2 column=info:age, timestamp=1502368069926, value=18
  13. 1 row(s) in 0.0120 seconds

# 统计表记录数

语法:count <table>, {INTERVAL => intervalNum, CACHE => cacheNum}

INTERVAL设置多少行显示一次及对应的rowkey,默认1000;CACHE每次去取的缓存区大小,默认是10,调整该参数可提高查询速度

例子:每隔一万行显示一条

  1. hbase(main):020:0> count 'OTTable', {INTERVAL => 10000, CACHE => 10}

#删除某 rowkey 的全部数据:

  1. hbase(main) > deleteall 'student','1001

#删除某 rowkey 的某一列数据:

  1. hbase(main) > delete 'student','1002','info:sex'

#删除一个列族,alter,disable,enable

我们之前建了3个列族,但是发现member_id这个列族是多余的,因为他就是主键,所以我们要将其删除。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ0OTMwMA_size_16_color_FFFFFF_t_70 3

报错,删除列簇的时候必须先将表给disable掉。

20190606161657731.png

再删除列簇‘member_id’

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ0OTMwMA_size_16_color_FFFFFF_t_70 4

该列族已经删除,我们继续将表enable

20190606161657783.png

#清空表数据

清空表的操作顺序为先 disable,然后再 truncating。

  1. hbase(main) > truncate 'student'

# drop一个表

Drop之前需要先disable

20190606161803619.png

20190606161803637.png

# 查询表是否存在

20190606161803654.png

# 判断表是否enable

20190606161803659.png

# 判断表是否disable

20190606161803688.png

四、Hbase读写流程及寻址

#写操作流程

(1) Client通过Zookeeper的调度,向RegionServer发出写数据请求,在Region中写数据。

(2) 数据被写入Region的MemStore,直到MemStore达到预设阈值。

(3) MemStore中的数据被Flush成一个StoreFile。

(4) 随着StoreFile文件的不断增多,当其数量增长到一定阈值后,触发Compact合并操作,将多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。

(5) StoreFiles通过不断的Compact合并操作,逐步形成越来越大的StoreFile。

(6) 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个新的Region。父Region会下线,新Split出的2个子Region会被HMaster分配到相应的RegionServer上,使得原先1个Region的压力得以分流到2个Region上。

可以看出HBase只有增添数据,所有的更新和删除操作都是在后续的Compact历程中举行的,使得用户的写操作只要进入内存就可以立刻返回,实现了HBase I/O的高机能。

#读操作流程

(1) Client访问Zookeeper,查找-ROOT-表,获取.META.表信息。

(2) 从.META.表查找,获取存放目标数据的Region信息,从而找到对应的RegionServer。

(3) 通过RegionServer获取需要查找的数据。

(4) Regionserver的内存分为MemStore和BlockCache两部分,MemStore主要用于写数据,BlockCache主要用于读数据。读请求先到MemStore中查数据,查不到就到BlockCache中查,再查不到就会到StoreFile上读,并把读的结果放入BlockCache。

寻址过程:client—>Zookeeper—>-ROOT-表—>.META.表—>RegionServer—>Region—>client

aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTQwMjM3OC8yMDE4MTIvMTQwMjM3OC0yMDE4MTIxMDIxNDcxNzkzNC03NjAyOTMxNDUucG5n

aHR0cDovL3d3dy5hYm91dHl1bi5jb20vZGF0YS9hdHRhY2htZW50L2ZvcnVtLzIwMTUwMS8xMy8xNjAxMDN3ZGR6emdsZGN4OGQydXh4LmpwZw

table在行的方向上分隔为多个Region。Region是HBase中分布式存储和负载均衡的最小单元,即不同的region可以分别在不同的Region Server上,但同一个Region是不会拆分到多个server上。

Region按大小分隔,每个表一行是只有一个region。随着数据不断插入表,region不断增大,当region的某个列族达到一个阈值(默认256M)时就会分成两个新的region。

每个region由以下信息标识:

  • <表名,startRowkey,创建时间>
  • 由目录表(-ROOT-和.META.)可值该region的endRowkey

HRegion定位:

Region被分配给哪个Region Server是完全动态的,所以需要机制来定位Region具体在哪个region server。

HBase使用三层结构来定位region:

  • 1、 通过zk里的文件/hbase/rs得到-ROOT-表的位置。-ROOT-表只有一个region。
  • 2、通过-ROOT-表查找.META.表的第一个表中相应的region的位置。其实-ROOT-表是.META.表的第一个region;.META.表中的每一个region在-ROOT-表中都是一行记录。
  • 3、通过.META.表找到所要的用户表region的位置。用户表中的每个region在.META.表中都是一行记录。

-ROOT-表永远不会被分隔为多个region,保证了最多需要三次跳转,就能定位到任意的region。client会讲查询的位置信息保存缓存起来,缓存不会主动失效,因此如果client上的缓存全部失效,则需要进行6次网络来回,才能定位到正确的region,其中蚕丝用来发现缓存失效,另外三次用来获取位置信息。

五、Hbase中的三个重要机制

# flush机制

当MemStore达到阈值,将Memstore中的数据Flush进Storefile

涉及属性:hbase.hregion.memstore.flush.size:134217728(即:128M就是Memstore的默认阈值)

hbase.regionserver.global.memstore.upperLimit:0.4

即:这个参数的作用是当单个HRegion内所有的Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模式来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。

hbase.regionserver.global.memstore.lowerLimit:0.38

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

# compact机制

把小的Memstore文件合并成大的Storefile文件。

# split机制

当Region达到阈值,会把过大的Region一分为二。

六、HFile

HBase中KeyValue数据的存储格式,是hadoop的二进制格式文件。首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。Trailer中又指针指向其他数据块的起始点,FileInfo记录了文件的一些meta信息。Data Block是hbase io的基本单元,为了提高效率,HRegionServer中又基于LRU的block cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定(默认块大小64KB),大号的Block有利于顺序Scan,小号的Block利于随机查询。每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成,Magic内容就是一些随机数字,目的是烦着数据损坏,结构如下。

  

aHR0cDovL3d3dy5hYm91dHl1bi5jb20vZGF0YS9hdHRhY2htZW50L2ZvcnVtLzIwMTUwMS8xMy8xNjAxMDRzb2pvMWoxdzAwZHRuajF5LmpwZw

HFile结构图如下:

  

aHR0cDovL3d3dy5hYm91dHl1bi5jb20vZGF0YS9hdHRhY2htZW50L2ZvcnVtLzIwMTUwMS8xMy8xNjAxMDV1eWFoYmExZTY0ZGtzeG40LmpwZw

Data Block段用来保存表中的数据,这部分可以被压缩。Meta Block段(可选的)用来保存用户自定义的kv段,可以被压缩.FileInfo段用来保存HFile的元信息,本能被压缩,用户也可以在这一部分添加自己的元信息。Data Block Index段(可选的)用来保存Meta Blcok的索引。Trailer这一段是定长的。保存了每一段的偏移量,读取一个HFile时,会首先读取Trailer,Trailer保存了每个段的起始位置(段的Magic Number用来做安全check),然后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不需要扫描整个HFile,而只需从内存中找到key所在的block,通过一次磁盘io将整个 block读取到内存中,再找到需要的key。DataBlock Index采用LRU机制淘汰。HFile的Data Block,Meta Block通常采用压缩方式存储,压缩之后可以大大减少网络IO和磁盘IO,随之而来的开销当然是需要花费cpu进行压缩和解压缩。目标HFile的压缩支持两种方式:gzip、lzo。

七、补充

1、补充1:

aHR0cDovL3d3dy5hYm91dHl1bi5jb20vZGF0YS9hdHRhY2htZW50L2ZvcnVtLzIwMTQxMi8yOC8xMzM4NDRmOTNlc29hdGpkdzF5OHQ5LnBuZw

我们观察上面这一幅图:

  1. 张表,有两个列族(红颜色的一个,黄颜色的一个),一个列族有两个列,从图中可以看出,这就是列式数据库的最大特点,同一个列族的数据在在一起的,我们还 发现如果是有多个版本,同时也会存多个版本。最后我们还发现里面存了这样的值:r1:键值,cf1:列族的名字,c1:列明。t1:版本号,value (最后一幅图说明的是value值可以存放的位置)。通过这样的看法,我们发现如果我们设计表的时候把这几个东西:r1:键值,cf1:列族的名 字,c1:列明的名字取短一点是不是我们会省出好多存储的空间!

还有,我们从这一幅图中还应该得到这样的认识:

我 们看倒数第二张图,字段筛选的效率从左到右明显下降,所以在keyvalue的设计时用户可以考虑把一些重要的筛选信息左移到合适的位置,从而在不改变数 据量的情况下,提高查询性能。那么简单的说就是用户应当尽量把查询维度或信息存储在行健中,因为它筛选数据的效率最高。

得到上面的认识后,我们应该还要会有这样的觉悟:

HBase 的数据存储时会被有顺序的存储到一个特定的范围,因为我们存储的时候一般都是按顺序的,所以会一直存到同一个region上,由于一个region只能由 一个服务器管理,这样我们老是添加到同一个region上,会造成读写热点,从而使集群性能下降。那么解决这个的办法还是有的,我能想到的就是,比如我们 有9台服务器,那么我们就回去当前时间,然后摸9,加到行健前缀,这样就会被平均的分到不同的region服务器上了,这样带来的好处是,因为相连的数据 都分布到不同的服务器上了,用户可以多线程并行的读取数据,这样查询的吞吐量会提高。关于我们版本的控制,我们要么就让多台服务器上的时间都同步,要么干脆就在put插入数据的时候,就设置一个客户端的时间戳来代替。(因为我们要是不显示的添加,人家就给我们在自己的服务器上添加了自己的时间了。)

补充2:

  1. 计表的时候,有两种设计方式,一种是高表设计,一种是胖表设计。根据HBase的拆分规则,我们的高表设计更容易拆分(使用组合键),不过,如果我们设计 成胖表,而我们的这个胖里的数据需要经常修改,这样设计是很合理的,因为我们的HBase保证了行级的原子性,如果设计成高表,反而就不合适了,因为不能 保证跨行的原子性。

补充3:

  1. 写缓存:每 一个put的操作实际上是RPC的操作,它将客户端的数据传送到服务器然后返回,这只适合小数据量的操作,如果有个应用程序需要每秒存储上千行数据到 HBase表中,这样处理就不太合适了。HBase的API配备了一个客户端的写缓冲区,缓冲区负责收集put操作,然后调用RPC操作一次性将put 往服务器。默认情况下,客户端缓冲区是禁止的。可以通过自动刷写设置为FALSE来激活缓冲区。 table.setAutoFlush(false);void flushCommits () throws IOException这个方法是强制 将数据写到服务器。用户还可以根据下面的方法来配置客户端写缓冲区的大小。 void setWritaeBufferSize(long writeBufferSize) throws IOException;默认大小是 2MB,这个也是适中的,一般用户插入的数据不大,不过如果你插入的数据大的话,可能要考虑增大这个值。从而允许客户端更高效地一定数量的数据组成一组通 过一次RPC请求来执行。给每个用户的HTable设置一个写缓冲区也是一件麻烦的事,为了避免麻烦,用户可以在Hbase-site.xml中给用户设置一个较大的预设值。

八、其他

#Hbase常见问题及解决方案汇总

#Hbase架构视频

#Hbase批量导入说明:https://cloud.tencent.com/developer/article/1446572

#Hbase查询快的原因:https://blog.csdn.net/haoshuai2015/article/details/80984544

#记一次Hbase查询速度优化经历

发表评论

表情:
评论列表 (有 0 条评论,326人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据存储HBase

    这两天要写一个方案,某单位想建一个中心数据库,汇聚各业务系统数据,以及各种网上抓取的预报数据。我设想是用HBase。 主要考虑点是: 1、开源 2、支持海量数据 该单