重点:HBase的基本数据模型、拓扑结构、部署配置方法,并介绍通过命令行和编程方式使用HBase的基本方法。
HBase:一种列存储模式与键值对相结合的NoSQL软件,但更多的是使用列存储模式,底层的数据文件采用HDFS存储,其文件结构和元数据等由自身维护.
HBase是Hadoop的重要成员,提供了分布式数据表和更高效的数据查询能力,弥补了HDFS只能进行文件管理以及MapReduce不适合完成实时任务的缺陷.
HBase利用HDFS实现数据分布式存储,数据分块以及多副本等,HBase在此基础上实现了对记录的更新和删除.Hbase具有灵活的数据模型,不仅可以基于键进行快速查询,还可以实现基于值、列名等的全文遍历和检索。
1、HBase概述
HBase的优点:
- 采用面向列加键值对的存储模式
- 可以实现便捷的横向扩展,对于元数据管理能力的扩展,可以通过数据分片的方式进行.
- 可以实现自动的数据分片.
- 可以实现严格的读写一致性和自动的故障转移
- 可以实现对全文的检索与过滤
- 支持通过命令行或者java/python等语言来进行操作
HIVE与HBase之间的关系,属于协作关系:
- 通过ETL工具将数据源抽取到HDFS存储;
- 通过Hive清洗、处理和计算原始数据;
- HIve清洗处理后的结果,如果是面向海量数据随机查询场景的可存入Hbase
- 数据应用从HBase查询数据;
HIVE |
HBASE |
HIVE数据仓库:Hive本质将HDFS中已经存储的文件在Mysql中做了一个双射关系,以方便使用HQL去管理查询。(主要用于解决数据处理和计算问题) |
HBASE数据库:一种面向列存储的非关系型数据库(主要用于解决实时数据查询问题) |
HIVE用于数据分析、清洗:HIVE是适用于离线的数据分析和清洗,延迟较高. |
HBASE用于存储结构化和非结构化的数据;HBASE适应于单表非关系型数据的存储,不适合做关联查询,类似JOIN等操作。比如:日志明细、交易清单、轨迹行为。 |
HIVE基于HDFS、MapReduce:HIVE存储的数据依旧在DataNode上,编写的HQL语句终将是转换为MapReduce代码执行 |
HBASE基于HDFS:数据持久化存储的体现形式是Hfile,存放于DataNode中,被RegionServer以region的形式进行管理。 |
延迟较低,接入在线业务使用:面对大量的企业数据,HBase可以直线单表大量数据的存储,同时提供了高效的数据访问数据。 |
2、HBase的数据结构模型
HBase逻辑数据模型:
HBase物理存储方式,其中StuInfo列族的物理存储方式:
HBase采用的是一种面向列的键值对存储模式。
表4-1中有2行记录,行键:001和002,其他所有信息均可以看做是行内记录的字段,或者列。
表中有2个列族(Column Family),列族的名字必是可显示的字符串。每个列族中含有若干列(Columns)。列族可以看做HBase表结构(Schema)的一部分,需要在建表时预先定义。
//eg,创建表:create'表名','列族名1', '列族名2', '列族名N' create 'table_test001','basic','test001'
对于面向列的存储模式,列则不属于表结构,HBase不会预先定义列名及其数据类型和值域等内容。每一条记录中的每个字段必须记录自己的列名(也称列标识符,Column qualifier)以及值(Value)的时间戳(Timestamp)。
这和关系型数据库有很大的不同,在关系型数据库中,表结构(Schema)是独立存储的。在关系型数据库中可能会采用以下方式进行记录:
Hbase并不需要预先设计列的结构,当添加新的列时,只需要在新记录中记录这个列名即可,不会对已有的数据产生任何影响。由于Hbase每条记录都记录了自己的列名,如果某一行数据不存在某个列,则不会记录该键值对。因此HBase不需要对不存在的数据项记录null值,使HBase可以支持数以万计的列名,且并不会在数据稀疏的情况下为NULL值消耗存储空间。
//eg,新增或修改表的值(数据):put ‘表名’, ‘行键’, ‘列族名’, ‘列值’或者 put ‘表名’, ‘行键’, ‘列族名:列名’, ‘列值’ put 'table_test001','001','basic','Micheal Jordan',1 put 'table_test001','001','basic:firstname','Kobe'
HBase中其中一个列族basic,列族basic的实际存储方式:
逻辑上的两行数据,在实际上被保存为8个键值对,或称为单元格(cell)。单元格是由行键、列名、值和时间戳来确定的最小存储单元。
键值对的实际存储方式:
每一行数据,或者说每一个键值对包含了行键(Row)、列族名称(Column Family)和列标识符(Column Qualifier),即列名、时间戳、行键类型(Key Type)与值(Value)。
键值对结构以两个固定长度的数值开始,分别表示 Key 的长度和 Value 的长度。紧接着是 Key,Key 以 RowLength 开始,是固定长度的数值,表示 RowKey 的长度;接着是 Row,然后是固定长度的数值 ColumnFamilyLength,表示 Family 的长度;之后是 Family 列族,接着是 Qualifier 列标识符,Key 最后以两个固定长度的数值 TimeStamp 和 Key Type(Put/Delete) 结束。Value部分没有这么复杂的结构,就是纯粹的二进制数据。
eg:
1)Name Space
命名空间,类似于关系型数据库的database概念,每个命名空间下有多个表,HBASE有两个自带的命名空间,分别是HBASE和default,HBASE中存放的是HBASE内置的表(系统内建表,包含namespace和meta表:存的是用户表或系统表位置信息),default表是用户默认的使用的命名空间(用户建表时未指定namespace的表都创建在此)。
HBase中的数据以表的形式存储。表名作为HDFS存储路径的一部分来使用,在HDFS中可以看到每个表名都作为独立的目录结构。
2)Region(表的切片)
Hbase 表的分片,HBase 表会根据 RowKey值被切分成不同的 region 存储在 RegionServer 中,在一个 RegionServer 中可以有多个不同的 region。
3)行(Row)
在HBase表里,每一行数据代表一个数据对象,每一行都有一个行键(Row Key)来进行唯一标识[不可分割的字节数组,且按照字典排序[ASCII码]由低到高存储],每一行都有一个rowkey和多个column(列)组成。在HBase中针对行键建立索引,提高检索数据的速度。
scan '表名' //扫描某个列族 scan '表名', {COLUMN=>'列族名'} //扫描某个列族的某个列 scan '表名', {COLUMN=>'列族名:列名'} //查询同一个列族的多个列 scan '表名', {COLUMNS => [ '列族名1:列名1', 列'族名1:列名2', …]}
4)Column
HBASE中的每个列都由column family(列族)和column qualifier(列限定符)进行限定,例如info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。列族是列的集合,列族中所有列成员有相同的前缀,列族的名字必须是可显示的字符串。
5)Store
每一个region有一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个ColumnFamily建一个store(即有几个ColumnFamily,也就有几个Store)。一个Store由一个memStore、0或多个StoreFile组成。
6)Time Stamp(时间戳)
用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入HBASE的时间。
7)Cell(单元格)
由{rowkey,column family:column qualifier,time stamp}唯一确定的单元,cell中的数据没有类型的,全部是字节码形式存储。
3、HBase的拓扑结构
HBase采用了主从式的拓扑结构。其主要组件包括一个主节点和若干个从节点(称为Reginsonserver或Hregionserver)。这里的主从节点都是服务器节点。HBase还需要借助Zookeeper集群来实现节点监控和容错。
1个Region Server 包含多个Region,每个Region里面包含一个或多个store(存储的实际数据)。
regionserver的作用:
- 数据的增删改查
- region的切分和合并,即使master挂掉也不影响查询数据。
hbase依赖于zookeeper,master的作用:表的增删改查、管理regionserver。
HBASE底层依赖于hdfs,还依赖于zookeeper,hbasemaster(管理ddl操作)会将某一部分工作交给zookeeper(zookeeper分担hbasemaster与client的交互,client从zookeeper中获取表region相关信息)。Regionserver(管理dml操作)维护一个个region,多个HRegion共享HLog(预写入日志,防止内存中数据丢失,用来做灾难恢复)。每一个region里面包含一个或多个store(逻辑上是表的列族,store存储上是分离的,实际数据存储在Men store(内存)和store file(磁盘)里面),每个store包含与其对应的MemStore以及一个或多个StoreFile (到达刷写条件后,从内存到磁盘会刷写数据,刷写后就会生成一个store file),hfile是一种文件格式,最后store file 和hlog都要通过hdfs client 交互存到hdfs。
1.Client
Client 包含了访问 Hbase 的接口,另外 Client 还维护了对应的 cache 来加速 Hbase 的访问,比如 cache 的.META.元数据的信息。
2.Zookeeper
HBase 通过 Zookeeper 来做 master 的高可用、RegionServer 的监控、元数据的入口以及 集群配置的维护等工作。
具体工作如下:
- 通过 Zoopkeeper 来保证集群中只有 1 个 master 在运行,如果 master 异常,会通过竞争机制产生新的master提供服务
- 通过 Zoopkeeper 来监控 RegionServer 的状态,当 RegionSevrer 有异常的时候,通过回调的形式通知 Master RegionServer 上下线的信息
- 通过 Zoopkeeper 存储元数据的统一入口地址
3.HMaster
HMaster的主要功能有:(分配region、管理regionserver)
- 为RegionServer分配Region
- 监控到region失效,并将失效的region分配到正常的regionserver上
- 维护整个集群(HRegionServer)的负载均衡。
- 维护集群的元数据信息
- 当regionserver失效,协调对应的Hlog拆分
注:HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行。
4.HRegionServer(维护Hregion处理请求、拆分Hregion)
HregionServer的主要作用:直接对用户的读写请求,功能如下:
- 处理来自客户端的读写请求
- 管理master为其分配的region
- 负责和底层HDFS的交互,存储数据到HDFS
- 负责region变大以后的拆分
- 负责storefile的合并工作
5.HRegion
Hbase 表的分片,HBase 表会根据 RowKey值被切分成不同的 region 存储在 RegionServer 中,在一个 RegionServer 中可以有多个不同的 region。
每个HRegion由多个Store构成,每个Store保存一个列族(Columns Family),表有几个列族,则有几个Store,每个Store由一个MemStore和多个StoreFile组成,MemStore是Store在内存中的内容,写到文件后就是StoreFile。StoreFile底层是以HFile的格式保存。
6.HLog
HLog(WAL log):WAL意为write ahead log(预写日志),用来做灾难恢复使用,HLog记录数据的变更,包括序列号和实际数据,所以一旦region server 宕机,就可以从log中回滚还没有持久化的数据。
Hlog存储在HDFS中。
7.Store = memstore + storefile=列族
一个 Store 对应 HBase 表中的一个列族。当一个列族的memstore触发持久化条件时,整个region里的store会全部进行持久化操作。Store中的数据持久化存储在HDFS上,其格式为Hfile。
手动持久化操作:flush '表名'
8.Memstore
每个store管理一块内存memstore,当用户写入键值对时,最终会将数据写入memstore,当memstore中的数据达到一定大小,或者一定时间,或者用户执行flush指令时,regionserver会将memstore中的数据按行键进行字典顺序排序,并持久化写入storefile中。
9.Storefile :包含Hfile
当memstore中的数据不断持久化,形成多个storefile.每个storefile是有序的,但storefile之间是无序的。
10.HFile
这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile 是以 Hfile 的形式存储在 HDFS 的。
HFile 文件是不定长的,长度固定的只有其中的两块:Trailer 和 File Info。Trailer 中有指针指向其他数据块的起始点,File Info 记录了文件的一些 Meta 信息。每个 Data 块的大小可以在创建一个 Table 的时候通过参数指定(默认块大小为 64KB)。每个 Data 块除了开头的 Magic 以外就是由一个键值对拼接而成的,Magic 内容是一些随机数字,用于防止数据损坏。
4、水平分区原理
HBase将大数据表水平分割,形成不同的region,并由不同的regionserver管理。原理:一个表刚建立时,只有一个数据分区,随着数据增多,根据一定规则将表进行水平拆分,形成2个分区或多个分区,而这些region无法存储到一台机器上时,就会分布存储在多台机器上。这些分区交给不同的regionserver管理。
分区的拆分是基于行键[行键按ASCII码进行排序]进行的,无论分区几次,属于哪个列族,相同的行键一定在一个region中。HBase的数据文件(HFile)是一种Map文件,即排序后的序列化文件。
HBase中每个Region由三个要素组成,包括Region所属的表、第一行、最后一行。第一个region没有首行,最后一个region没有末行。每个region都有一个regionID来标识它的唯一性,Region标识符就可以表示成:表名+开始行键+RegionID.
4.1 Meta表
Meta表记录各个Regionserver所管理的表和分区。meta表也会随着数据增多,进行自动分区,每个meta分区记录一部分用户表和分区管理情况。
表和分区的分级管理机制:用户进行读写数据时,会根据需要读写的表和行键,通过如下顺序寻找该行键对应的分区:Zookeeper->META->Regionserver->Region.
META表的入口地址存储在Zookeeper集群,表的实体由若干个Regionserver进行管理(持久化在HDFS上)。Meta节点并不负责存储这些信息,客户端在寻址之后,可以将信息缓存。
有了Region标识符,就可以唯一标识每个Region。为了定位每个Region所在的位置,可以构建一张映射表[元数据表,又称为Meta表],包含了关于Region的元数据。映射表:region标识符+region服务器标识符。
4.2 读流程
首先往hbase写数据,client put一条数据,会先去zk里面找mete表所在的regionserver位置,假设返回meta表在regionserver102中,client再请求regionserver102返回要put数据的表的regionserver位置(此时客户端会把元数据缓存下来,下次先找缓存,没有再找zk)。再假设要put数据对应的表的在regionserver103上,此时client发送请求到regionsever103上,先写预写入日志,再写入内存,此时对于客户端来说,写就结束了,不需要等到flush,然后就通知下客户端写操作结束。
4.3 数据写入机制
HBase通过Regionserver来管理写入。Regionserver负责向对应的表分区和列族中写入数据,管理缓存和排序,以及实现容错。
每个Regionserver可能管理多个表和多个分区。Regionserver将用户请求的数据对应写到表分区(Hregion)中,每个分区中有一个或多个store,每个store对应当前分区中的一个列族。
比之前多了一个block cache(读缓存),缓存的是实际数据,client get查询一条数据,会先去zk里面找mete表所在的regionserver位置,假设返回meta表在regionserver102中,client再请求regionserver102返回要get数据的表的regionserver位置,再假设要get数据对应的表的在regionserver103上,此时client发送get请求到regionsever103上,读取内存、block cache以及磁盘,先将磁盘中的数据缓存至block cache中,方便下次查询,再比较时间戳选择时间戳最新的返回。
//演示: Test表已有中rowkey行info列族name列值为“zhangsan”,先刷写到磁盘:flush “test” 查看磁盘中hfile:./hbase org.apache.hadoop.hbase.io.hfile.HFile -a -b -e -k -p -f /home/hbase/data/default/test/9cc2c91ace3d7a1d8f88aa95f4867206/info/ 再修改zhangsan为lisi:put "test","rowkey","info:name","lisi",time stamp(小于修改数据的时间戳) 再查看表中数据为张三还是李四:scan “test”。 数据写入机制的相关配置:通过hbase-site.xml中进行多种配置,可以实现memstore[大小]和持久化[阻塞系统]等机制的管理和优化。
5、数据表的基本设计原则
- 行键分布尽量均匀
- 行键和列族名尽量短
- 列族尽量少
6、HBase的基本操作(HBase shell)
命名 |
描述 |
语法 |
help '命令名' |
查看命令的使用描述 |
help '命令名' |
whoami |
我是谁 |
whoami |
version |
返回hbase版本信息 |
version |
status |
返回hbase集群的状态信息 |
status |
table_help |
查看如何操作表 |
table_help |
create |
创建表 |
create '表名', '列族名1', '列族名2', '列族名N' |
alter |
修改列族 |
添加一个列族:alter '表名', '列族名' |
删除列族: |
||
describe |
显示表相关的详细信息 |
describe '表名' |
list |
列出hbase中存在的所有表 |
list |
exists |
测试表是否存在 |
exists '表名' |
put |
添加或修改的表的值 |
put '表名', '行键', '列族名', '列值' |
put '表名', '行键', '列族名:列名', '列值' |
||
scan |
通过对表的扫描来获取对用的值 |
scan '表名' |
扫描某个列族: scan '表名', {COLUMN=>'列族名'} |
||
扫描某个列族的某个列: scan '表名', {COLUMN=>'列族名:列名'} |
||
查询同一个列族的多个列: scan '表名', {COLUMNS => [ '列族名1:列名1', '列族名1:列名2', …]} |
||
get |
获取行或单元(cell)的值 |
get '表名', '行键' |
get '表名', '行键', '列族名' |
||
count |
统计表中行的数量 |
count '表名' |
incr |
增加指定表行或列的值 |
incr '表名', '行键', '列族:列名', 步长值 |
get_counter |
获取计数器 |
get_counter '表名', '行键', '列族:列名' |
delete |
删除指定对象的值(可以为表,行,列对应的值,另外也可以指定时间戳的值) |
删除列族的某个列: delete '表名', '行键', '列族名:列名' |
deleteall |
删除指定行的所有元素值 |
deleteall '表名', '行键' |
truncate |
重新创建指定表 |
truncate '表名' |
enable |
使表有效 |
enable '表名' |
is_enabled |
是否启用 |
is_enabled '表名' |
disable |
使表无效 |
disable '表名' |
is_disabled |
是否无效 |
is_disabled '表名' |
drop |
删除表 |
drop的表必须是disable的 |
disable '表名' |
||
drop '表名' |
||
shutdown |
关闭hbase集群(与exit不同) |
|
tools |
列出hbase所支持的工具 |
|
exit |
退出hbase shell |
7、什么场景适合HBase
半结构化或非结构化数据:数据结构字段不够确定或杂乱无章很难按一个概念去进行抽取的数据。
记录非常稀疏:RDBMS的行有多少列是固定的,为null的列浪费了存储空间。HBase为null的Column不会被存储,节省空间的同时又提高了读性能。
多版本数据:如上文提到的根据Row key 和Column key定位到的Value可以有任意数量的版本值,因此对于需要存储变动历史记录的数据。
超大数据量:RDBMS数据库撑不住,就出现了读写分离策略,通过一个master专门负责写操作,多个Slave负责读操作,服务器成本倍增。随着压力增加,master撑不住,就需要分库,把关联不大的数据分开部署,join查询用不上,需要借助中间层。随着数据量的进一步增加,一个表的记录越来越大,查询就变得很慢,于是又得搞分表,比如按ID取模分成多个表以减少单个表的记录数。