相识Hbase,只看这一篇足以

打印 上一主题 下一主题

主题 887|帖子 887|积分 2661

1、关于HBase

1.1、Hbase是什么?

HBase的官网所在: https://hbase.apache.org/ 标记是一个漂亮的虎鲸

官网上对HBase的先容非常的简朴直接:
  1. Apache HBase™ is the Hadoop database, a distributed, scalable, big data store.
复制代码
Apache HBase是一个Hadoop上的数据库。一个分布式,可扩展的大数据存储引擎。HBase有个最明显的特征:
1、HBase支持非常大的数据集,数十亿行*数百万列。如此庞大的数据量级,足以撑爆我们在J2EE阶段学习过的全部数据存储引擎。
2、HBase支持大数据量的随机、及时读写操纵。在海量数据中,可以实现毫秒级的数据读写。
3、HBase从一开始就深度集成了Hadoop。HBase基于Hadoop进行文件长期化,还继承了Hadoop带来的强盛的可扩展性。Hadoop可以基于自制PC机组建庞大的应用集群。HBase也深度集成了Hadoop的MapReduce计算框架,而且也正在积极整合Spark。这使得HBase能够很轻松的融入到整个大数据生态圈。
4、HBase的数据是强一致性的,从CAP理论来看,HBase是属于CP的。这种设计可以让步伐员不须要担心脏读、幻读这些事务终极一致性带来的问题。
5、最后最紧张的还是HBase的框架性能是足够高效的。HBase的开源社区非常活跃,他的性能颠末很多大型商业产品的验证。Facebook的整个消息流转的基础设施就构建于HBase之上。
可以说正是Hadoop+HBase一起提供的强悍无敌的性能,支持起了大数据的整个技能体系。这就好比必须要先有操纵系统,能够通过文件、内存来存储资料,后面开发出来的各种应用步伐才有用武之地。
聊到了Hadoop+HBase,就不得不说Google发布的三篇论文。恒久以来,各大互联网公司都在注重构建服务,而对于业务数据,很难达到海量这个级别。而对于海量数据的处理,还只是Google如许的搜索引擎巨头在进行深入的研究。
2003年,Google发布Google File System论文,(GFS)这是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于自制的普通硬件上,提供容错功能。从根本上说:文件被分割成很多块,使用冗余的方式储存于商用机器集群上。
紧随厥后,2004年,Google公布了MapReduce论文,论文描述了大数据的分布式计算方式,主要思想是将任务分解然后在多台处理本领较弱的计算节点中同时处理,然后将结果合并从而完成大数据处理。
接着到了2006年,Google公布了BigTable论文,BigTable是一种构建于GFS和MapReduce之上的多维希罕图管理工具。
正是这三篇论文,掀起了开源软件的大数据热潮。人们根据GFS,开发出了HDFS文件存储。MapReduce计算框架,也成了海量数据处理的尺度。而HDFS与MapReduce结合在一起,形成了Hadoop。而BigTable更是启发了无数的NoSQL数据库。而HBase正是继承了正统的BigTable思想。所以,Hadoop+HBase是模拟了Google处理海量网页的三大基石实现的,他们也就成了开源大数据处理的基石。
1.2、HBase的数据结构

HBase也可以作为一个数据库使用,但是为了应对海量数据,他存储数据的方式与我们理解的传统关系型数据库有很大的区别。固然他也有表、列如许的逻辑结构,但是团体上,他是以一种k-v键值对的方式来存储数据的。

纵向来看,HBase中的每张表由Rowkey和若干个列族或者称为列簇组成。此中Rowkey是每一行数据的唯一标识,在对数据进行管理时,必须自行保证Rowkey的唯一性。接下来HBase依然会以差别的列来管理数据,但是这些列分别归属于差别的列簇。在HBase中,同一张表的数据,只须要保证列簇是相同的,而列簇下的
列,可以是不相同的。所以由此可以扩展出非常多的列。在HBase中,对于同一张表,不发起界说过多的列簇,通常不要超过三个。而更多的数据,可以以列的方式来扩展。
从横向来看,HBase中的记录,会划分为一个一个的Region,存储在差别的RegionServer上。而且会在差别的RegionServer之前形成备份,以Region为单位提供了故障后自动恢复的机制
最后,从团体来看,HBase固然还是以HDFS作为文件存储,但是他存储的数据不再是简朴的文本文件,而是颠末HBase优化压缩过的二进制文件.,所以他的存储文件通常是不能够直接查看的。
1.3、HBase的基础架构

HBase团体的基础架构如下图:

此中,


  • client客户端包含了访问HBase的接口,别的也维护了对应的缓存来加速对HBase的访问。
  • RegionServer直接对接用户的读写请求,是真正干活的节点。他会将数据以StoreFile的情势存储到差别的HDFS目录中。
  • HMaster主要是维护一些集群的元数据信息,同时监控RegionServer的服务状态,而且通过Zookeeper提供集群服务,向客户端暴露集群的服务端信息
1.4、HBase适用场景

这里不妨拿Hive来比较一下,有助于我们理解HBase的适用场景。
Hive提供了基于SQL的对海量数据进行查询统计的功能,但是Hive不存储数据,全部数据操纵都是对HDFS上的文件进行操纵,所以他对数据的查询操纵能做的优化比较有限。同时Hive也无法直接受理数据,对数据的管理依靠于MapReduce,所以延迟非常高。所以Hive通常只适用于一些OLAP的场景,而且通常是与其他组件结合一起进行使用。
而HBase与Hive的区别就非常明显。HBase基于HDFS来存储数据,但是他存储的数据都是颠末自己优化索引后的数据,所以他对数据的存储是非常高效的,比HDFS直接存储文件的性能要高很多,可以作为整个大数据的存储基石。而HBase以雷同于Redis的列式存储来管理数据,对数据的增删改都会非常高效,可以达到毫秒级相应。同时,也提供了完善的客户端API,所以他完全可以作为传统意义上的数据库使用,适用于大部分的OLTP的场景。但是他的缺点也比较明显,基于列式存储的数据,天生就不太适合大规模的数据统计,所以在很多OLAP的场景,须要结合其他一些组件如spark、hive等,来提供大规模数据统计的功能。
2.HBase安装

2.1、实验环境与前置软件

实验环境:准备了三台服务器,预装CentOS7,分别分配机器名
hadoop01,hadoop02,hadoop03。IP所在不紧张,由于集群都会通过机器名来配置。三台服务器须要配置时间同步,这个很紧张。预备在hadoop01机器上安装HBase的HMaster,所以须要配置hadoop01到三台机器的免密登录。
  1. 免密登录的配置方式: 在hadoop01上生成秘钥: ssh-keygen。然后将密钥分发到不同的机器 ssh-copy-id hadoop02。这样在hadoop01机器上,使用ssh hadoop02就不需要再输入密码了。
复制代码
前置必须安装的软件:JDK8;Hadoop采用3.2.2版本;Spark采用3.1.1版本。
可选的软件:hadoop01上安装mysql服务,三台机器上安装spark集群,采用3.1.1版本。在hadoop02机器上安装hive3.1.2版本,并部署hive服务hiveserver2。(这些软件不是必须的,但是我们在学习HBase时会用到)
2.2、安装Zookeeper

HBase须要依靠Zookeeper来进行集群内的数据和谐。固然HBase的发布包中内置了Zookeeper,但是在搭建集群时,通常会别的搭建一个Zookeeper集群,减少组件之间的直接依靠。
到Zookeeper官网下载zookeeper3.5.8版本(zookeeper版本不能低于3.4.x)。apache-zookeeper-3.5.8-bin.tar.gz。上传到服务器后,解压到/app/zookeeper目录。然后进入zookeeper的conf目录。
  1. mv zoo_sample.cfg zoo.cfg
  2. vi zoo.cfg
  3. 修改zookeeper的数据目录
  4. dataDir=/app/zookeeper/data
  5. 增加集群节点配置
  6. server.1=hadoop01:2888:3888
  7. server.2=hadoop02:2888:3888
  8. server.3=hadoop03:2888:3888
复制代码
修改完成后,将zookeeper目录团体分发到集群中的三台服务器上。然后须要在zookeeper的数据目录/app/zookeeper/data中增长一个myid文件。文件的内容就是当前服务器在zookeeper中的节点ID。例如Hadoop01机器的myid是1,
Hadoop02机器的myid是2,
Hadoop03机器的myid是3。
配置完成后,启动zookeeper集群: bin/zkServer.sh start。 启动完成后,使用jsp指令能看到一个QuorumPeerMain历程,体现集群启动乐成。
2.3、安装HBase

可以到 https://hbase.apache.org/downloads.html 页面下载对应的HBase。选择HBase版本时,须要注意的是要与Hadoop版本兼容。这是如今官网上贴出的hadoop版本兼容表格:

我们这次就选择如今最新的HBase2.4.4版本。(其实要注意,在工作中通常都不发起使用最新的版本)
2.4、搭建HBase集群模式

下载HBase部署包: hbase-2.4.4-bin.tar.gz,上传到服务器hadoop01。
1、解压后移动到/app/hbase目录。
  1. tar -zxvf hbase-2.4.4-bin.tar.gz
  2. mv hbase-2.4.4 /app/hbase
复制代码
进入hbase的conf配置目录,修改配置文件。
2、先修改hbase-env.sh。在文件最后加入如下配置:
  1. export JAVA_HOME=/app/java/jdk1.8.0_212
  2. #是否使用HBase内置的ZK。默认是true。
  3. export HBASE_MANAGES_ZK=false
复制代码
3、修改hbase-site.xml,加入如下配置
  1. <configuration>
  2.     <property>
  3.         <name>hbase.rootdir</name>
  4.         <value>hdfs://hadoop01:8020/hbase</value>
  5.     </property>
  6.     <property>
  7.         <name>hbase.cluster.distributed</name>
  8.         <value>true</value>
  9.     </property>
  10.     <property>
  11.         <name>hbase.master.port</name>
  12.         <value>16000</value>
  13.     </property>
  14.     <property>
  15.         <name>hbase.zookeeper.quorum</name>
  16.         <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
  17.     </property>
  18.     <property>
  19.         <name>hbase.zookeeper.property.dataDir</name>
  20.         <value>/app/zookeeper/data</value>
  21.     </property>
  22. </configuration>
复制代码
这里配置了一个hbase在hdfs上的根目录。这个目录不须要手动创建,而且最好保证这个目录在HDFS上不存在。
4、配置regionservers文件,在文件里列出集群中全部的节点。
  1. hadoop01
  2. hadoop02
  3. hadoop03
复制代码
5、同步Hadoop的配置文件。为了保证文件内容一致,采用软连接的方式。
  1. ln -s /app/hadoop/hadoop-3.2.2/etc/hadoop/core-site.xml /app/hbase/hbase-2.4.4/conf/
  2. ln -s /app/hadoop/hadoop-3.2.2/etc/hadoop/hdfs-site.xml /app/hbase/hbase-2.4.4/conf/
复制代码
6、将hbase团体分发到其他服务器
  1. scp -r /app/hbase/ root@hadoop02:/app
  2. scp -r /app/hbase/ root@hadoop03:/app
复制代码
7、配置Hbase环境变量。将HBase安装目录配置到环境变量中,并将bin目录配置到PATH环境变量
  1. vi ~/.bash_profile
  2. 增加 export HBASE_HOME=/app/hbase/hbase-2.4.4 并在PATH变量中增加 $HBASE_HOME:BIN
复制代码
8、启动hbase
  1. bin/start-hbase.sh
复制代码
启动完成后,使用jps指令能够查看到在当前机器上启动了HMaster历程。三台机器上都启动了HRegionServer历程。
然后可以访问HBase的管理页面: http://hadoop01:16010 查看集群的状态。

注意
1、三台服务必须要进行时钟同步,否则启动hbase时会报错。抛出ClockOutOfSyncException非常。
2、在官方文档中提到, HBase是基于Hadoop的,所以HBase内置了Hadoop的jar包。但是jar包的版本要跟现实部署的hadoop一致。例如我们使用的HBase2.4.4版本,内置的Hadoop的jar包版本都是2.10.0的。在现实部署时,最好使用hadoop集群中的jar包。
3、部署完成后,可以在HDFS上看到HBase的元数据。/hbase
3、HBase基础操纵

关于HBase的使用,在官网上有一个很紧张的文档,下载所在 https://hbase.apache.org/apache_hbase_reference_guide.pdf 这是学习HBase最好的资料。不过这个资料是全英文的,而且内容非常非常多,须要有肯定的理解本领。
3.1、基础指令

3.1.1、使用HBase的客户端

  1. #查看HBase基础指令
  2. [root@192-168-65-174 hbase-2.4.4]# bin/hbase --help
  3. #hbase命令行
  4. [root@192-168-65-174 hbase-2.4.4]# bin/hbase shell
  5. #查看帮助
  6. hbase:001:0> help
  7. #列出已有的表
  8. hbase:002:0> list
复制代码
3.1.2、基础的表操纵

  1. #创建表 user表,有一个列簇 basicinfo
  2. hbase:003:0> create 'user','basicinfo'
  3. #插入一条数据
  4. hbase:004:0> put 'user','1001','basicinfo:name','roy'
  5. hbase:005:0> put 'user','1001','basicinfo:age',18
  6. hbase:006:0> put 'user','1001','basicinfo:salary',10000
  7. #插入第二条数据
  8. hbase:007:0> put 'user','1002','basicinfo:name','sophia'
  9. hbase:008:0> put 'user','1002','basicinfo:sex','female'
  10. hbase:009:0> put 'user','1002','basicinfo:job','manager'
  11. #插入第三条数据
  12. hbase:005:0> put 'user','1003','basicinfo:name','yula'
  13. hbase:006:0> put 'user','1003','basicinfo:school','phz school'
复制代码
3.1.3、数据操纵

  1. #按照RowKey,查找单条记录
  2. hbase:007:0> get 'user','1001'
  3. hbase:011:0> get 'user','1001','basicinfo:name'
  4. #按照版本查询数据,每次put都会给这条数据的VERSIONS+1
  5. hbase:031:0> get 'user','1001',{COLUMN => 'basicinfo:name',VERSIONS=>3}
  6. #使用scan,查询多条记录
  7. hbase:008:0> scan 'user'
  8. hbase:009:0> scan 'user',{STARTROW => '1001',STOPROW => '1002'}
复制代码
1、HBase查询数据只能依据Rowkey来进行查询,而Rowkey是由客户端直接指定的,所以在使用HBase时, Rowkey如何设计非常紧张,要带上紧张的业务信息。
2、scan指令后面的查询条件,STARTROW和STOPROW是必须大写的。查询的结果是左开右闭的。
3、其他查询数据的操纵可以使用help ‘get’ 或者 help ‘scan’,来查看更多的查询方式。例如对数据进行过滤
  1. #查看表结构 这个结果很重要。列出了列簇的所有属性。
  2. hbase:012:0> desc 'user'
  3. Table user is ENABLED
  4. user
  5. COLUMN FAMILIES DESCRIPTION
  6. {NAME => 'basicinfo', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS=> '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE',COMPRESSION => 'NONE', TTL => 'FOREVER',MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536',REPLICATION_SCOPE => '0'}
  7. #查询表中的记录数。
  8. hbase:013:0> count 'user'
  9. #修改表结构
  10. hbase:019:0> alter 'user',{NAME => 'basicinfo',VERSIONS => 3}
  11. hbase:020:0> desc 'user'
  12. COLUMN FAMILIES DESCRIPTION{NAME => 'basicinfo', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS=> '3', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE',COMPRESSION => 'NONE', TTL => 'FOREVER',MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536',REPLICATION_SCOPE => '0'}
  13. #删除某一列
  14. delete 'user','1002','basicinfo:sex'
  15. #删除某一条数据
  16. deleteall 'user','1003'
  17. #清空表数据
  18. truncate 'user'
  19. #删除表 删除之前需要先disable。
  20. disable 'user'
  21. drop 'user'
复制代码
desc指令中能够看到Hbase的表有很多紧张的属性,这些属性都对应HBase底层维护数据的一些方式。例如COMPRESSION 属性指定了HBase的数据压缩格式,默认是NONE,别的还可以修改为JDK内置的GZ或者Hadoop集成的LZ4,别的还可以配置其他的属性。详细可以参看官方文档中的Appendix D部分。
3.2、HBase的数据结构

RowKey
Rowkey是用来检索记录的唯一主键,雷同于Redis中的key。访问HBase中的表数据,只能通过Rowkey来查找。访问HBase的数据只有三种方式:


  • 通过get指令访问单个rowkey对应的数据。
  • 通过scan指令,默认全表扫描
  • 通过scan指令,指定rowkey的范围,进行范围查找。
Rowkey可以是恣意字符串,最大长度是64KB,现实中通常用不到这么长。在HBase内部,Rowkey保存为字节数组。存储时,会按照Rowkey的字典次序排序存储。
在现实使用时,对Rowkey的设计是很紧张的,每每须要将一些经常读取的紧张列都包含到Rowkey中。而且要充分思量到排序存储这个特性,让一些干系的数据尽量放到一起。
比如我们创建一个用户表,经常会按用户ID来查询, 那Rowkey中肯定要包含用户ID字段。而如果把用户ID放在Rowkey的开头,那数据就会按照用户ID排序存储,查询Rowkey时效率就会比较快。
Column Family(列簇) 与 Column(列)
HBase中的列都是归属于某一个列簇的,HBase在表界说中只有对列簇的界说,没有对列的界说。也就是说,列是可以在列簇下随意扩展的。要访问列,也必须以列簇作为前缀,使用冒号进行连接。列中的数据是没有范例的, 全部都是以字节码情势存储。同一个表中,列簇不宜界说过多。
物理上,一个列簇下的全部列都是存储在一起的。由于HBase对于数据的索引和存储都是在列簇级别进行区分的,所以,通常在使用时,发起一个列簇下的全部列都有大致相同的数据结构和数据大小,如许可以提高HBase管理数据的效率。
Versions:
在HBase中,是以一个{row,column,version}如许的数据唯一确定一个存储单元,称为cell。在HBase中,大概存在很多cell有相同的row和column,但是却有差别的版本。多次使用put指令,指定相同的row和column,就会产生同一个数据的多个版本。
默认环境下,HBase是在数据写入时以时间戳作为默认的版本,也就是用scan指令查找数据时看到的timestamp内容。HBase就是以这个时间戳降序排列来存储数据的,所以,HBase去读取数据时,默认就会返回最近写入的数据。客户端也可以指定写入数据的版本,而且这个版本并不要求严格递增。
当一个数据有多个版本时,HBase会保证只有最后一个版本的cell数据是可以查询的,而至于其他的版本,会由HBase提供版本接纳机制,在某个时间进行删除。
例如:以下指令可以指定要存储多少个版本
  1. #给basicinfo声明最多保存5个版本
  2. alter 'user',NAME => 'basicinfo',VERSIONS=>5
  3. #指定最少两个版本
  4. alter 'user',NAME => 'basicinfo',MIN_VERSIONS => 2
  5. #查询多个版本的数据。
  6. get 'user','1001',{COLUMN => 'basicinfo:name',VERSIONS => 3}
  7. #查询10个历史版本
  8. scan 'user',{RAW => true, VERSIONS => 10}
复制代码
put与delete时,也可以指定版本。详细可以使用help指令查看。
别的,在使用scan查询批量数据时,Hbase会返回一个已经排好序的结果。按照 列=>列簇=>时间戳 的次序进行排序。也可以在scan时,指定逆序返回。
Namespace 定名空间
在创建表的时候,还可以指定表所属的定名空间。例如
  1. create_namespace 'my_ns'
  2. create 'my_ns:my_table','fam'
  3. alter_namespace 'my_ns',{METHOD => 'set','PROPERTY_NAME' =>'PROPERTY_VALUE'}
  4. list_namespace
  5. drop_namespace 'my_ns'
复制代码
在HBase中,每个定名空间会对应HDFS上的/hbase/data目录下的一个文件夹。差别定名空间的表存储是隔离的。
在HDFS上可以看到, HBase默认创建了两个定名空间,一个是hbase,这是系统的定名空间,用来存放HBase的一些内部表。另一个是default,这个是默认的定名空间。不指定定名空间的表都会创建在这个定名空间下。
4、HBase原理

4.1、HBase文件读写框架

对HBase有了肯定的相识了之后,再回头来看一下之前先容过的HBase的架构图,对HBase的团体结构就可以有更深入的理解。

1、StoreFile
现实保存数据的物理文件,StoreFile以HFile的情势存储在HDFS上,每个Store会有一个或多个StoreFile,数据在每个StoreFile内都是有序的。在HDFS/hbase/data/default/user目录下。
2、MemStore
写缓存。由于HFile中的数据要求是有序的,所以数据是先存储在MemStore中,排好序后,比及达刷写时机才会写入到HFile。每次刷写都会形成一个新的HFile。
3、WAL
由于数据要颠末MemStore排序后才能刷写到HFile,但是数据在内存中会有很高的概率丢失。为相识决这个问题,数据会先写在一个叫做Write-Ahead-logfile的文件中,然后再写入MemStore中。当系统出现故障时,就可以从这个日志文件进行重建。
4.2、HBase写数据流程

HBase写数据的流程如下图:

1、client向HReginoServer发送写请求
2、HRegionServer将数据写到WAL.
3、HRegionServer将数据写到内存MemStore
4、反馈Client写乐成
图中meta表信息为hbase默认维护的一个表。 可以用scan ‘hbase:meta’指令查看。他维护了系统中全部的Region信息。这个表的HDFS路径会维护在Zookeeper中。这个表里的info:server字段就保存了Region所在的机器以及端口。
客户端要写入的数据,现实上写入到MenStore就算完成了,HBase会在后续的过程中定期将MemStore内的数据写入到StoreFile中。在客户端可以通过flush指令手动触发这一过程。
HBase刷写MemStore的几个时机:


  • MemStore级别限定
    habse.hregion.memstore.flush.size 默认128M。 当Region中恣意一个MemStore的大小达到这个上限,就会触发一次MemStore操纵,生成一个HFile。
  • Region级别限定:
    hbase.hregion.memstore.block.multiplier 默认4. 当Region中全部MemStore的大小总和达到上限,也会触发MemStore的革新。这个上限就是hbase.hregion.memstore.flush.size * habse.hregion.memstore.flush.size
  • RegionServer级别限定
    对整个RegionServer里写入的全部MemStore数据大小,配置了一个低水位阈值和高水位阈值。 当所MemStore文件大小达到低水位阈值时,会开始强制执行flush。并按照MemStore文件从大到小依次刷写。而当所MenStore文件大小达到高水位时,就会壅闭全部的写入操纵,强制执行flush。直到总MenStore大小降落到低水位阈值
    涉及到两个参数:hbase.regionserver.global.memstore.size 体现RegionServer的高水位阈值。默认配置None。分配JVM的Heap堆内存大小的40%(0.4)。 老版本的参数是hbase.regionserver.global.memstore.upperLimit
    hbase.regionserver.global.memstore.size.lower.limit 体现RegionServer的低水位占据高水位阈值的百分比。 默认配置也是None,体现是高水位阈值的95%(0.95)。老版本的参数是hbase.regionserver.global.memstore.lowerLimit
  • WAL级别限定
    hbase.regionserver.maxlogs WAL数目上限。当RegionServer的WAL文件数目达到这个上下后,系统就会选取最早的Hlog对应的一个或多个Region进行Flush。这时候会在日志中有一条报警信息 Too many WALs。count=…
  • 定期革新MemStore
    hbase.regionserver.optionalcacheflushinterval 默认是3600000 单位是毫秒,即1个小时。这是HBase定期革新MemStore的时间间隔。通常在生产中,为了尽量保证业务性能会将这个参数配置为0,体现关闭定时自动刷写。
  • 手动调用flush执行
    在HBase的刷写机制下,只有RegionServer达到高水位阈值时才会壅闭写入操纵,对业务产生直接影响。其他的几个限定级别都不会产生壅闭,但是通常还是会对性能产生肯定的影响。所以在很多生产系统中,会根据业务的希望环境定制MemStore文件刷写计谋。比如在业务不繁忙的时候进行定期手动刷写。
4.3、HBase读数据流程

HBase读数据的流程大致如下:

1、Client先访问zookeeper,从meta表读取region的位置,然后读取meta表中的数据。meta中存储了用户表的region信息。
2、根据namespace、表名和rowkey在meta表中找到对应的region信息。
3、找到这个Region对应的Regionserver
4、读取数据时,会启动多个StoreFileScanner和一个MemStoreScanner,终极的结果会同时读取内存和磁盘中的数据,并按照数据的版本号也就是时间戳,获取最新的一条数据返回给客户端。
5、在读取StoreFile时,为了提高读取数据的效率,会构建一个BlockCache作为读缓存。MemStore和StoreFile中查询到的目标数据,都会先缓存到BlockCache中,再返回给客户端。
从团体上来看, HBase写数据的操纵只须要把数据写入内存就算完成,反而读数据要从文件开始读。所以,对于HBase,会出现出写数据比读数据更快的效果。
4.4、HBase文件压缩流程

上面从HBase读写文件的流程简朴的推出了一个结论:HBase的写操纵会体现得比读操纵更快。但是如果在面试过程中问个为什么,如许简朴的流程推导显然无法让面试官信服。这个时候,就须要更加深入HBase的底层,寻找答案。
4.4.1 HBase底层的LSM树

HBase的每个Region中存储的是一系列可搜索的键值映射,底层会以LSM树(Log Structured Merge Tree )的结构来对key进行索引LSM树也是B-树的一种扩展,很多NoSQL数据库都会采用这种LSM树来存储数据。
LSM树的基础思想是将对数据的修改增量保存在内存中,当内存达到指定大小限定后,将这些修改操纵批量写入磁盘。如许写的性能得到极大的提升,不过读取的时候就会稍微贫困一些,须要合并磁盘中的隶属数据和内存中最近修改的操纵。
LSM树通过这种机制,将一棵大树拆分成N棵小树,这些小树起首写入内存。随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树再异步定期进行merge操纵,终极将数据合并成一棵大树,以优化读性能。
在HBase中,由于小树要先写入内存,为了防止内存数据丢失,写内存的同时须要暂时长期化到磁盘(HBase的磁盘对应HDFS上的文件 ),这就对应了HBase的MemStore和Hlog。MemStore对应一个可变的内存存储,记录了最近的写(put)操纵。当MemStore上的树达到肯定大小后,就须要进行flush操纵,如许MemStore就变成了HDFS上的磁盘文件StoreFile。
之前先容过,HBase是将全部数据修改存储为单独的版本,因此,对同一个key,会有多个版本保留在MemFile和StoreFile中,这些过时的数据是有冗余的,HBase会定期对StoreFile做merge合并操纵,彻底删除无效的空间,多棵小树在这个时候合并成大树,来增强读性情。
在HBase 2.0版本中,还引入了一个紧张的机制IN_MEMORY Compact内存压缩,来优化LSM中的内存树。内存压缩是HBase2.0中的一个紧张特性,通过在内存中引入LSM结构,减少多余的数据,实现降低flush频率和减少MemFile刷写数据的效果。详细可以参看一个官方的博客:https://blogs.apache.org/hbase/entry/accordion-hbase-breathes-with-in。
在HBase2.0中,可以通过修改hbase-site.xml来对内存压缩方式进行统一配置
  1. <property>
  2. <name>hbase.hregion.compacting.memstore.type</name>
  3. <value><none|basic|eager|adaptive></value>
  4. </property>
复制代码
或者也可以对某个列簇进行单独的设定:
  1. alter 'user',{NAME=>'basicinfo',IN_MEMORY_COMPACTION=>'<none|basic|eager|adaptive>'}
复制代码
内存压缩有四种模式可以选择。默认是basic。
别的一个eager模式,会在内存中过滤重复的数据,这也意味着eager模式相比basic模式,内存过滤时会有额外的性能开销,但是刷写文件时的数据量会相对较小。
eager模式更适合于数据大量镌汰的场景,比如MQ、购物车等。
而别的一个adaptive是一个实验性的选项,其基本作用就是自动判断是否须要启用eager模式。
4.4.2、HBase文件压缩过程

HBase使用LSM树的方式,可以将应用步伐级别的随机IO转换成为次序磁盘IO,对于写性能的提升非常明显。但是LSM树对读数据的性能影响也是非常大的。所以,团体上,相比于MySQL的B+树结构,HBase的写性能会比MySQL高很多,同时读性能又会比MySQL低很多。别的,LSM结构是一种append-only-tree,文件不支持修改,只支持添加,这也正贴合Hadoop的文件机构,再加上HBase设计时,所面对的是TB级别的数据量,这种机制基本也就成了必选的方式了。
而HBase也对读操纵做了肯定的优化。例如,为了加快对MemFile映射的内存做数据读取,HBase会构建一个布隆过滤器,对内存中的数据进行快速过滤,从而减少对内存的搜索。这也就对应了desc指令中看到的BLOOMFILTER属性。
在HBase中,flush指令,将内存中的数据刷写到HDFS上。这时,只刷写,不过滤数据。每次刷写都会在HDFS上新刷写一个文件。
别的compact和major-compact两个指令,会用来将文件进行合并。
  1. hbase:016:0> help 'compact'
  2. Compact all regions in passed table or pass a region row
  3. to compact an individual region. You can also compact a single column
  4. family within a region.
  5. You can also set compact type, "NORMAL" or "MOB", and default is "NORMAL"
  6. Examples:
  7. Compact all regions in a table:
  8. hbase> compact 'ns1:t1'
  9. hbase> compact 't1'
  10. Compact an entire region:
  11. hbase> compact 'r1'
  12. Compact only a column family within a region:
  13. hbase> compact 'r1', 'c1'
  14. Compact a column family within a table:
  15. hbase> compact 't1', 'c1'
  16. Compact table with type "MOB"
  17. hbase> compact 't1', nil, 'MOB'
  18. Compact a column family using "MOB" type within a table
  19. hbase> compact 't1', 'c1', 'MOB'
  20. hbase:015:0> help 'major_compact'
  21. Run major compaction on passed table or pass a region row
  22. to major compact an individual region. To compact a single
  23. column family within a region specify the region name
  24. followed by the column family name.
  25. Examples:
  26. Compact all regions in a table:
  27. hbase> major_compact 't1'
  28. hbase> major_compact 'ns1:t1'
  29. Compact an entire region:
  30. hbase> major_compact 'r1'
  31. Compact a single column family within a region:
  32. hbase> major_compact 'r1', 'c1'
  33. Compact a single column family within a table:
  34. hbase> major_compact 't1', 'c1'
  35. Compact table with type "MOB"
  36. hbase> major_compact 't1', nil, 'MOB'
  37. Compact a column family using "MOB" type within a table
  38. hbase> major_compact 't1', 'c1', 'MOB'
复制代码
这个compact指令是将相邻的部分小文件合并成大文件,而且他不会删除过时的数据,所以性能消耗不会太大而major-compact指令是将全部的storefile文件合并成一个大文件,这时他就会删除过时的数据,就会消耗很多的机器性能。
当我们发送一个delete指令删除一个列时,HBase并不会直接删除数据,而是给数据添加一个删除标记,如许客户端就查不到当火线的值。而在flush阶段,只会删除那一列最新版本的数据,但是删除标记同样不会删除,以保证历史的版本不会让客户端查询出来。compact阶段,由于数据依然没有统一,所以删除标记依然不会删除,以保证客户端始终查不到历史版本的数据。只到major-compact阶段,数据全部合并到一个StoreFile中时,才会将历史版本的数据以及删除标记一起删除。
   差别版本的数据可以使用下面的指令查看。包含历史版本以及删除标记。
hbase> scan ‘t1’, {RAW => true, VERSIONS => 10}
  HBase提供了按照HFile文件大小以及文件个数,定时触发compact和major-compact的机制。例如 hbase.hregion.majorcompaction这个参数就用来配置自动文件压缩的时间间隔
   但是这个参数在生产环境一般都是发起设置为0,关闭的。由手动来定时触发major-compact操纵。这是由于文件压缩须要对数据做大量的合并和删除,会影响线上的性能。所以通过定时脚本保证集群在晚上业务不太繁忙时进行major-compact。
  如果storeFile文件过大,HBase还会有别的的机制将storefile重新拆分成几个大小符合的文件,即Region,分到差别的RegionServer上。所以团体上,如果HBase的数据操纵频繁, 你可以看到他的文件是分久必合合久必分,经常变来变去的。
5、HBase客户端

HBase支持多种客户端的API操纵。从官方文档中可以看到, HBase支持Rest、Thrift、C/C++、Scala、Jython多种客户端,此中这个Jython比较有趣,是Python语言的一种纯JAVA实现,团体上兼容了JAVA和Python。
我们这次重点就关注HBase的Rest和Java两种API。
5.1、Rest API

基础服务指令:
  1. #前台启动
  2. bin/hbase rest start -p <port>
  3. #后台启动 日志记录在$HBASE_LOGS_DIR
  4. bin/hbase-daemon.sh start rest -p <port>
  5. #停止Rest服务
  6. bin/hbase-daemon.sh stop rest
复制代码
这里要注意下HBase的Rest服务默认的启动接口是8080,这个接口用得非常多,非常容易端口冲突。spark默认就会使用这个端口。所以可以另行指定一个端口,比如9090。
HBase提供了非常丰富的Rest端口来管理HBase,这里列出了几个比较常用的端口。详细全部的窗口可以查看官方文档。
Rest所在HTTP方法意义
/status/clusterGET查看集群状态
/namespacesGET查看hbase的定名空间。后面可以带上详细的定名空间,查看某个namespace的详情
/namespaces/{namespace}/tablesGET列出定名空间下的全部表。
/namespaces/{namespace}POST创建定名空间
/namespaces/{namespace}DELETE删除定名空间。定名空间必须是空的才可以删除。
/{table}/schemaGET查看表结构。同时POST请求更新表结构,PUT创建或更新一张表。DELETE删除表
/{table}/regionsGET查看表所在的region
/{table}/{row_key}PUT往表中写入一条数据。
/{table}/{row_key}/{column:qualifier}/{version_id}DELETE删除表中某一个历史版本数据
5.2、Java API

HBase作为一个NoSQL的数据库,他的客户端就设计得相称简朴了。构建HBase客户端,须要引入Maven依靠:
  1. <dependency>
  2. <groupId>org.apache.hbase</groupId>
  3. <artifactId>hbase-server</artifactId>
  4. <version>2.4.4</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.hbase</groupId>
  8. <artifactId>hbase-client</artifactId>
  9. <version>2.4.4</version>
  10. </dependency>
复制代码
然后就可以调用HBase提供的API进行数据操纵了。主要记住几个关键对象即可。例如对HBase的全部操纵都基于Connection对象。表结构管理都通过HBaseAdmin对象来操纵,表数据都通过Table对象来操纵。其他的代码不用强记,用到那边点那边就行。 只不过要注意下,HBase2.X版本的源码中有非常多已颠末时的类,如今版本委曲还可以用,但是源码中很多都会提示在将来的3.X版本会删除,所以尽量使用不过时的类。
1、获取HBase管理实例
  1. public Connection getConfig() throws IOException {
  2.     final Configuration configuration = HBaseConfiguration.create();
  3.     // 必选参数
  4.     configuration.set("hbase.zookeeper.quorum", "hadoop01,hadoop02,hadoop03");
  5.     configuration.set("hbase.zookeeper.property.clientPort", "2181");
  6.     // 其他可选参数
  7.     configuration.set("hbase.client.scanner.timeout.period", "10000");
  8.     configuration.set("hbase.hconnection.threads.max", "0");
  9.     configuration.set("hbase.hconnection.threads.core", "0");
  10.     configuration.set("hbase.rpc.timeout", "60000");
  11.     return ConnectionFactory.createConnection(configuration);
  12. }
复制代码
2、判断表是否存在
  1. // 查询表是否存在。默认使用 default 命名空间。其他命名空间需要传入 {namespace}:tableName
  2. public Boolean isTableExist(Connection connection, String tableName) throws IOException {
  3.     final HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
  4.     return admin.tableExists(TableName.valueOf(tableName));
  5. }
复制代码
3、创建表
  1. public void createTable(Connection connection, String tableName, String... columnFamilys) throws IOException {
  2.     final HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
  3.     if (columnFamilys.length <= 0) {
  4.         System.out.println("请设置列簇信息");
  5.     } else if (admin.tableExists(TableName.valueOf(tableName))) {
  6.         System.out.println("表" + tableName + "已经存在。");
  7.     } else {
  8.         // 2.0.0 版本新的 API。官网说明 2.0.0 版本有很多 API 已经过时,预计会在 3.0 版本后删除。如 HTableDescriptor 等
  9.         TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName));
  10.         List<ColumnFamilyDescriptor> families = new ArrayList<>();
  11.         for (String columnFamily : columnFamilys) {
  12.             final ColumnFamilyDescriptorBuilder cfdBuilder = ColumnFamilyDescriptorBuilder.newBuilder(ColumnFamilyDescriptorBuilder.of(columnFamily));
  13.             final ColumnFamilyDescriptor cfd = cfdBuilder.setMaxVersions(10).setInMemory(true).setBlocksize(8 * 1024).setScope(HConstants.REPLICATION_SCOPE_LOCAL).build();
  14.             families.add(cfd);
  15.         }
  16.         final TableDescriptor tableDescriptor = builder.setColumnFamilies(families).build();
  17.         admin.createTable(tableDescriptor);
  18.     }
  19. }
复制代码
4、删除表
  1. public void dropTable(Connection connection, String tableName) throws IOException {
  2.     final HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
  3.     if (admin.tableExists(TableName.valueOf(tableName))) {
  4.         admin.disableTable(TableName.valueOf(tableName));
  5.         admin.deleteTable(TableName.valueOf(tableName));
  6.         System.out.println("表" + tableName + "成功删除");
  7.     } else {
  8.         System.out.println("表" + tableName + "不存在");
  9.     }
  10. }
复制代码
5、向表中插入数据
  1. public void addRowData(Connection connection, String tableName, String rowkey, String columnFamily, String column, String value) throws IOException {
  2.     Table table = connection.getTable(TableName.valueOf(tableName));
  3.     // 构建 Put 指令,rowkey 必传
  4.     Put put = new Put(Bytes.toBytes(rowkey));
  5.     // 指定版本时间戳
  6.     put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), System.currentTimeMillis(), Bytes.toBytes(value));
  7.     put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
  8.     // 也可以传入一个 List<Put> 插入多列数据
  9.     table.put(put);
  10.     // 表操作之后记得要 close,关闭线程池
  11.     table.close();
  12. }
复制代码
6、根据rowkey删除数据
  1. public void deleteMultiRow(Connection connection, String tableName, String... rows) throws IOException {
  2.     Table table = connection.getTable(TableName.valueOf(tableName));
  3.     List<Delete> deletes = new ArrayList<>();
  4.     for (String row : rows) {
  5.         Delete delete = new Delete(Bytes.toBytes(row));
  6.         deletes.add(delete);
  7.     }
  8.     if (deletes.size() > 0) {
  9.         table.delete(deletes);
  10.     }
  11.     table.close();
  12.     System.out.println("表数据删除成功");
  13. }
复制代码
7、Get获取某一行数据
  1. public void getRowData(Connection connection, String tableName, String row) throws IOException {
  2.     Table table = connection.getTable(TableName.valueOf(tableName));
  3.     Get get = new Get(Bytes.toBytes(row));
  4.     final Result result = table.get(get);
  5.     for (Cell cell : result.rawCells()) {
  6.         System.out.println("rowkey: " + Bytes.toString(CellUtil.cloneRow(cell)));
  7.         System.out.println("  列簇:" + Bytes.toString(CellUtil.cloneFamily(cell)));
  8.         System.out.println("  列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
  9.         System.out.println("  值:" + Bytes.toString(CellUtil.cloneValue(cell)));
  10.         System.out.println("  版本时间戳:" + cell.getTimestamp());
  11.         System.out.println("==========================");
  12.     }
  13.     table.close();
  14. }
复制代码
8、Scan获取全部数据
  1. public void scanRowData(Connection connection, String tableName, String startRow, String stopRow) throws IOException {
  2.     Table table = connection.getTable(TableName.valueOf(tableName));
  3.     Scan scan = new Scan();
  4.     if (null!= startRow &&!"".equals(startRow)) {
  5.         scan.withStartRow(Bytes.toBytes(startRow));
  6.     }
  7.     if (null!= stopRow &&!"".equals(stopRow)) {
  8.         // 包含最后一行数据。 默认是左开右闭。
  9.         scan.withStopRow(Bytes.toBytes(stopRow), true);
  10.     }
  11.     ResultScanner scanner = table.getScanner(scan);
  12.     for (Result result : scanner) {
  13.         for (Cell cell : result.rawCells()) {
  14.             System.out.println("rowkey: " + Bytes.toString(CellUtil.cloneRow(cell)));
  15.             System.out.println("  列簇:" + Bytes.toString(CellUtil.cloneFamily(cell)));
  16.             System.out.println("  列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
  17.             System.out.println("  值:" + Bytes.toString(CellUtil.cloneValue(cell)));
  18.             System.out.println("  版本时间戳:" + cell.getTimestamp());
  19.             System.out.println("==========================");
  20.         }
  21.         System.out.println("-----------------------");
  22.     }
  23.     table.close();
  24. }
复制代码
6、HBase 优化

6.1、列簇设计

在设计HBase的表结构时,官网发起,一张表的列簇不要太多,最好是一个列簇。现实项目中,列簇最多也不要超过三个。由于纵向来看,HBase是以列簇为单位来构造文件,对应HDFS中一个单独的目录。如果列簇过多,会造成小文件太多,增长文件频繁合并拆分时的性能消耗。而从横向来看,当表的数据量比较大时,HBase会将一张表拆分成多个Region。如果列簇太多,容易产生数据倾斜问题,即某些热点Region的数据特别多,读写操纵也特别多。而其他Region的数据特别少,读写也非常少,如许就会造成性能消耗。所以官方发起每个表就是单列簇就行了,如许HBase对数据文件的管剖析更为高效。而如果须要多个列簇的话,尽量保证各个列簇的数目能够均匀增长。
6.2、预分区与Rowkey设计

默认环境下,HBase对大的StoreFile随机的进行Region划分,但是如许划分是非常不靠谱的,很容易产生数据倾斜的问题。所以,在现实使用时,一般都会进行预分区。这种方式就是手动维护一个region的划分规则,对每个region中的rowkey分布范围进行配置。如果加入的数据符合某个region维护的rowkey范围,就将数据交给这个region维护。用如许的方式,我们可以将数据所要存放的分区提前大致的维护好,以提高HBase的性能。
1、手动划分分区
  1. create 'staff','info',SPLITS=>['1000','2000','3000','4000']
复制代码
这种分区方式会按照Rowkey的前四位进行分区。例如152033333如许的rowkey,就会被分到 1000~2000 的这个分区内。
2、按照文件分区
创建一个splits.txt,文件内容如下:
  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
复制代码
然后执行建表语句
  1. create 'staff2','info',SPLITS_FILE=>'splits.txt'
复制代码
这种分区方式就会按照rowkey的第一个数字划分为10个分区。
3、生成十六进制序列预分区
  1. create 'staff3','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
复制代码
这几种方式中,第二种方式一般是用得最多的,第三种方式很少用。
4、在JavaAPI中也可以指定分区。在admin.createTable()方法中有两个重载的方法
  1. /**
  2. * @param desc table descriptor for table
  3. * @param startKey beginning of key range
  4. * @param endKey end of key range
  5. * @param numRegions the total number of regions to create
  6. */
  7. void createTable(TableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions) throws IOException;
  8. /**
  9. * @param desc table descriptor for table
  10. * @param splitKeys array of split keys for the initial regions of the table
  11. */
  12. default void createTable(TableDescriptor desc, byte[][] splitKeys) throws IOException {}
复制代码
分区完成后,可以在HBase的管理页面看到表的分区环境。

对分区的规划一般都会参考集群中节点的数目,一般来说,每个节点上的Region数目不要超过3个。
6.3、Rowkey设计

Rowkey是用来查找HBase表数据的唯一方式,所以Rowkey要包含将来关键的查询属性。
例如一个用户表,将来须要按照userName和userPass两个字段查询,那rowkey就应该以这两个字段为主要元素,计算出一个唯一的序列号作为rowkey。
另一方面,当一条数据有了rowkey后,这条数据存储于哪个分区,就取决于rowkey处在哪个分区内了。而我们对rowkey进行设计的目的,主要就是为了能够让数据尽量均匀的分布于全部的region上。如许能在肯定程度上防止数据倾斜。但是业务数据的rowkey分布每每不是按照预分区的规则均匀分布的。所以通常须要对rowkey进行肯定的散列,以帮助有肯定规则的数据能够尽量平均的分配到差别的region中。例如我们之前将staff表分为5个分区,而rowkey的最靠前部分userid很有大概都会合在1000~2000如许的范围内,那如许数据倾斜就非常明显了。一般来说,有几下几种常见的散列方式。
1、hash散列
对每个根据关键业务信息计算出来的原始rowkey进行MD5、AES之类的hash计算。
2、字符串翻转
将整个原始rowkey按照字符次序进行翻转。 例如13579,反转成为97531。
3、字符串拼接
在原始rowkey的前面添加一个干系的字符串来控制分区。例如原始rowkey计算成为0000{rowkey}。
6.4、Hive与HBase集成

Hive,也是大数据常用的一个数据仓库软件。他可以将SQL语句转换成为MapReduce计算任务,然后直接索引HDFS上的数据文件。如许就可以以SQL的方式进行大数据的查询分析任务。
HBase与Hive的特性在很多方面恰恰形成互补,天生就适合集成在一起使用。
1、HBase和Hive都是基于HDFS来存数数据。
2、Hive由于须要启动MapReduce计算,所以是非常慢的。而HBase的数据读写操纵非常快。
3、HBase只能根据Rowkey来检索数据,对业务场景的限定还是比较大的,而Hive可以通过SQL的方式对数据进行更机动的检索。

所以在很多项目中,都会将Hive和HBase集成在一起使用。利用HBase高效的读写性能,来提供OLTP的快速数据服务,同时利用Hive快速全面的SQL功能,提供OLAP的数据统计服务。
Hive与HBase集成的基本情势是在Hive中创建一个表,并指定映射成HBase中的一张表。而Hive中对这张关联表的操纵都会改为索引HBase表中的数据。
Hive与HBase集成步调:
1、Hive与HBase的版本兼容性
这是很紧张的一个条件。在搭建大数据项目时,组件之间的版本兼容性是一个非常头疼的问题。通常会使用CDH来统一解决各个组件的版本兼容。而我们如今全是使用Apache的开源版本组件,就须要我们自己来处理版本兼容问题。
我们使用的Hive版本是3.1.2。这个版本的Hive中默认已经集成了HBase。他集成的HBase版本是2.0.0-alpha4。而我们使用的HBase版本是2.4.4。颠末测试,发现这个版本的HBase是可以乐成集成Hive的。如果版本之间存在冲突的话,我们下面的实验是无法乐成的。这个时候须要下载Hive的源码,自行更新HBase的版本进行更新。
   1、关于Hive中的HBase版本,可以查看$HIVE_HOME/lib目录下的hbase开头的几个jar包。
2、编译Hive时,可以下载Hive源码,然后修改Hive中对HBase的maven依靠版本,然后使用maven重新编译,并将Hive中的HBase干系的jar包更新到Hive部署环境中。
  2、配置HBase环境变量
在之前每次启动hive客户端时都会有一个错误日志。
  1. which: no hbase in
  2. (:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/es/node-
  3. v8.1.0-linux-x64/bin:/app/hadoop/hadoop-3.2.2//bin:/app/hadoop/hadoop-
  4. 3.2.2//sbin:/app/java/jdk1.8.0_212//bin:/root/bin)
复制代码
这就是由于Hive在启动时会检测有没有安装HBase。可以在Hive的安装节点下编辑HBase的环境变量。
  1. vi ~/.bash_profile
  2. 在文件中添加
  3. export HBASE_HOME=/app/hbase/hbase-2.4.4
  4. 然后将$HBASE_HOME/bin目录添加到Path环境变量中。
复制代码
如许,再次启动Hive,就没有这个错误日志了。Hive执行过程中打印在控制台的日志量也大了很多。
3、在Hive的hive-site.xml配置文件中增长配置
  1. <property>
  2. <name>hive.zookeeper.quorum</name>
  3. <value>hadoop01,hadoop02,hadoop03</value>
  4. </property>
  5. <property>
  6. <name>hive.zookeeper.client.port</name>
  7. <value>2181</value>
  8. </property>
复制代码
增长关于HBase使用的Zookeeper集群的配置,如许Hive就可以像HBase一样去寻找Region,读取HDFS上的数据了。
4、Hive中创建映射表
  1. CREATE TABLE hive_hbase_emp_table(
  2. empno int,
  3. ename string,
  4. job string,
  5. mgr int,
  6. hiredate string,
  7. sal double,
  8. comm double,
  9. deptno int)
  10. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
  11. WITH SERDEPROPERTIES ("hbase.columns.mapping" =
  12. ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
  13. TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
复制代码
  此中 TBLPROPERTIES中的hbase.table.name体现要映射到HBase中的表。这个表要是在HBase中不存在的。Hive在建表时会在HBase上同时建表。
SERDEPROPERTIES中的hbase.columns.mapping指定了Hive中的hive_hbase_emp_table表与HBase中的hbase_emp_table表的对应关
系。其配置方式就按照字段一个一个映射。比如上面的配置中, :key是一个固定写法,体现HBase表中的Rowkey。而配置中的:key就对应Hive表中的第一个字段empno。后面的info:name就对应hive中的ename字段。后面依次配置各个字段的对应关系。
  5、往Hive中插入测试数据
接下来,可以实验往Hive的hive_hbase_emp_table表中使用insert语句插入数据进行实验。并在HBase对应的表中查数据。
不过要注意,对于Hive,不能使用load指令的方式直接导入数据。如许导入的数据将无法正确映射到HBase中。但是我们可以找一张Hive的中间表,以insert intoxxx select的方式来导入数据。
  1. CREATE TABLE emp(
  2. empno int,
  3. ename string,
  4. job string,
  5. mgr int,
  6. hiredate string,
  7. sal double,
  8. comm double,
  9. deptno int)
  10. row format delimited fields terminated by ',';
复制代码
然后在这个表中,可以导入我们在文本文件中预先准备的测试数据
  1. hive> load data local inpath '/root/emp' into table emp;
复制代码
接下来,通过中间表往映射表中插入数据
  1. hive> insert into table hive_hbase_emp_table select * from emp;
复制代码
6、检查测试结果
如果没有报错话,将可以在Hive和HBase两边查看到导入的数据。也可以在Hive中执行一些须要计算的复杂SQL。例如 select deptno,sum(sal) from emp groupby deptno;然后我们可以去HDFS上查看下数据是如何构造的。在HDFS上,可以看到,Hive中创建了一个/user/hive/warehouse/hive_hbase_emp_table目录,这是Hive中的表对应的目录。但是这个目录中是没有数据文件的。再到HBase对应的HDFS目录下去看一下。HDFS中同样创建了一个HBase表对应的目录 /hbase/data/default/hbase_emp_table 。
这个目录下会创建一个随机字符串定名的Storefile目录。进入这个录/hbase/data/default/hbase_emp_table/c4440f96cf62f5394722fd28bef9a734 可以看到HBase中的数据文件。这时会发现这个目录下的数据文件依然是空的。这就是由于如今HBase表中的数据还在内存当中,而从Hive中映射过来的数据并没有及时写入到Memfile。当在HBase中执行 flush ‘hbase_emp_table’ 指令后,HBase内存中的数据才会刷写到HDFS中。例如在HDFS的/hbase/data/default/hbase_emp_table/c4440f96cf62f5394722fd28bef9a734/info目录下可以看到一个随机字符串定名的文件,这就是HBase的落地文件。可以看到,Hive与HBase的集成,还是由HBase来保存数据,而此时Hive依然是通过MapReduce计算步伐来统计HBase中的数据。
   其实从这里可以看到, HBase是深度集成了Hadoop的。固然HBase落地到HDFS上的是二进制文件,不能直接读取,但是通过Hadoop的
MapReduce,可以很轻松的读到HBase中的数据。关于MapReduce如何读取HBase中的数据,其实寻常用得会比较少。在官网的先容文档
apache_hbase_reference_guide.pdf中,也有详细的说明。别的在HBase中,也提供了详细的示例代码,我们课程中就不再多先容了。
别的,还有别的一个紧张的框架,Spark。 Spark与HBase的集成,也是HBase中非常紧张的一个特性。有兴趣的同砚可以自行参考下。
最后,关于HBase的SQL查询支持,还有别的一个框架可以提供。Phoenix,官网所在: https://phoenix.apache.org/ 。这个框架比较小
众,如今的使用场景就限于给HBase提供尺度的JDBC支持。然后搭建完成后,须要使用一个SQuirrel SQL的客户端工具来连接。
  最后,在Hive中删除这个表,HBase中对应的表也跟着一起删除了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表