为什么是HBase
关系数据库已经流行很多年,并且Hadoop已经有了HDFS和MapReduce,为什么需要HBase
-
Hadoop是一个高容错、高延时的分布式文件系统和高并发的批处理系统,不适用于提供实时计算; -
HDFS面向批量访问模式,不是随机访问模式。 -
传统的通用关系型数据库无法应对在数据规模剧增时导致的系统扩展性和性能问题(分库分表也不能很好解决)。
-
传统关系数据库在数据结构变化时一般需要停机维护;空列浪费存储空间。
-
HBase是可以提供实时计算的分布式数据库,数据被保存在HDFS分布式文件系统上,由HDFS保证期高容错性。 -
在生产环境中,
HBase是如何基于Hadoop提供实时性呢?HBase上的数据是以StoreFile(HFile)二进制流的形式存储在HDFS上block块儿中;但是HDFS并不知道的HBase存的是什么,它只把存储文件是为二进制文件,也就是说,HBase的存储数据对于HDFS文件系统是透明的。
数据模型
Hbase是一个稀疏、多维度、排序的映射表,这张表的索引是行键、列族、列限定符和时间戳。
- 每个值是一个未经解释的字符串,没有数据类型。用户在表中存储数据,每一行都有一个可排序的行键和任意多的列。
- 表在水平方向由一个或多个列族组成,一个列族中可以包含任意多个列,同一个列族里面的数据存储在一起。
- 列族支持动态扩展,可以很轻松地添加一个列族或列,无需预先定义列的数量以及类型,所有列均以字符串形式存储,用户需要自行进行数据类型转换。
HBase中执行更新操作时,并不会删除数据旧的版本,而是生成一个新的版本,旧的版本仍然保留(这是和HDFS只允许追加不允许修改的特性相关的)
表:HBase采用表来组织数据,表由行和列组成,列划分为若干列族。
行:每个HBase表都由若干行组成,每个行由行键(row key)来标识。
列族:支持动态扩展,保留旧的版本,无所谓是否冗余存储;一个HBase表被分组成许多“列族”(Column Family)的集合,它是基本的访问控制单元。
列限定符:列族里的数据通过限定符(列)来定位。
单元格:在HBase表中,通过行、列族和列限定符确定一个“单元格”cell,单元格中存储的数据没有数据类型,总被视为字节数组byte[]
时间戳:每个单元格都保存着同一份数据的多个版本,这些版本采用时间戳进行索引。
存储结构
-
行式存储结构 适用于事物型操作比较多的情况,每次都生成一个完整记录,为了取出某一列数据,必须先去扫描数据库的第一行,然后这行中的某一字段取出来,再去扫描第二行;分析某一特征,都是针对一个列去分析,这样用行式存储就效率低了
-
列式存储结构 是用于分析型应用,按一个列去存储,一个列中通常数据类型比较相似,可以带来很高的数据压缩率
实现原理
HBase的功能组件
库函数 一般用于链接每个客户端
Master服务器 充当管家
- 分区信息进行维护和管理
- 维护了一个
Region服务器列表 - 整个集群当中有哪些
Region服务器在工作 - 负责对
Region进行分配 - 负载平衡
Region服务器 负责存储不同的Region - 一张大表被分为很多
Region,由Region服务器进行维护和管理 - 客户端直接向
Region服务器存取数据 客户端并不依赖于Master去获得位置信息。
一个HBase表被划分为多个Region,一个Region会分裂成多个新的Region
一个Region的大小最佳配置为1GB到2GB,Region的实际大小,取决于单台服务器的有效处理能力;对于同一个Region绝对不会拆分到不同的Region服务器上去;每一个Region服务器,大概能存储10到1000个Region
Region被存到哪里去了
HBase设计了三层结构实现Region的寻址和定位
首先要构建一个元数据表,假设这个元数据表只有两列,第一列是Region的id,第二列是Region服务器的id
HBase是最开始构建时有一个映射表,这个映射表被称为.META.表,用来存储元数据
| 层次 | 名称 | 作用 |
|---|---|---|
| 第一层 | Zookeeper文件 | 记录了-ROOT-表的位置信息 |
| 第二层 | -ROOT-表 | 记录了.META.表的Region位置信息 -ROOT-表只能有一个Region 通过-ROOT-表,就可以访问.META.表中的数据 |
| 第三层 | .META.表 | 记录了用户数据表的Region位置信息 .META.表可以有多个Region 保存了HBase中所有用户数据表的Region位置信息 |
准备HBase组件
在node1上执行
> tar -zxvf /home/extend_tools/hbase-2.1.0-bin.tar.gz -C /home/modules/
> ls /home/modules/ | grep hbase
hbase-2.1.0
# 修改 hbase-env.sh 文件
> vim /home/modules/hbase-2.1.0/conf/hbase-env.sh
# 在最后一行补充以下内容:
export JAVA_HOME=/usr/lib/jvm/java
修改hbase-site.xml配置文件
> vim /home/modules/hbase-2.1.0/conf/hbase-site.xml
在<configuration>至</configuration>中添加如下内容请记得修改桶名称
<property>
<name>hbase.rootdir</name>
<value>obs://stu-fygod/hbasetest001</value>
</property>
<property>
<name>zookeeper.session.timeout</name>
<value>120000</value>
</property>
<property>
<name>hbase.zookeeper.property.tickTime</name>
<value>6000</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/home/modules/hbase-2.1.0/data/zookeeper</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>node2,node3,node4</value>
</property>
<property>
<name>hbase.tmp.dir</name>
<value>/home/modules/hbase-2.1.0/tmp</value>
</property>
<property>
<name>hbase.wal.provider</name>
<value>org.apache.hadoop.hbase.wal.FSHLogProvider</value>
</property>
<property>
<name>hbase.wal.dir</name>
<value>hdfs://node1:8020/hbase</value>
</property>
<property>
<name>hbase.client.write.buffer</name>
<value>5242880</value>
</property>
<property>
<name>hbase.regionserver.handler.count</name>
<value>200</value>
</property>
<property>
<name>hbase.hstore.compaction.min</name>
<value>6</value>
</property>
<property>
<name>hbase.hregion.memstore.block.multiplier</name>
<value>16</value>
</property>
<property>
<name>hfile.block.cache.size</name>
<value>0.2</value>
</property>
配置Regionserver
> vim /home/modules/hbase-2.1.0/conf/regionservers
# 删除默认的 localhost,然后添加内容如下为:
node1
node2
node3
node4
同步Hadoop的配置
需要注意复制完后重新配置core-site.xml文件
# 拷贝 Hadoop 的 core-site.xml 配置文件至 hbase/conf 目录中
> cp /home/modules/hadoop-2.8.3/etc/hadoop/core-site.xml /home/modules/hbase-2.1.0/conf/
# 根据实际情况添加
<property>
<name>fs.obs.access.key</name>
<value>Q9SJLOXAQIQNVDLFC7CS</value>
</property>
<property>
<name>fs.obs.secret.key</name>
<value>c8URca7UYpDwRQR0pAf6WhvxWlDjPnl9RRcWtRoR</value>
</property>
<property>
<name>fs.obs.endpoint</name>
<value>obs.cn-north-4.myhuaweicloud.com</value>
</property>
替换旧版依赖包
HBase 的 2.1.0 版本默认的 Jar 包是 Hadoop 2.7.4 版本,我们需要替换成 2.8.3 版本的
> rm -rf /home/modules/hbase-2.1.0/lib/*
> cp -r /home/extend_tools/hbase_lib/* /home/modules/hbase-2.1.0/lib/
分发组件
在node1上执行
> for i in {2..4};do scp -r /home/modules/hbase-2.1.0 root@node${i}:/home/modules/;done
# 拷贝完毕后,在 `node2~node4` 节点,均可查看到如下目录
> ls /home/modules/ | grep hbase
hbase-2.1.0
# 配置 HBase 的环境变量
> vim /etc/profile
添加
export HBASE_HOME=/home/modules/hbase-2.1.0
export PATH=$HBASE_HOME/bin:$PATH
启动HBase
在node1上执行
> start-hbase.sh
结果
node1: HMaster、HRegionServer、HquorumPeer
node2: HRegionServer、HquorumPeer
node3: HRegionServer、HquorumPeer
node4: HRegionServer、HquorumPeer
node1执行 HBase shell
在 node1 节点执行 HBase shell 进入客户端。
hbase shell
···
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
Version 2.1.0, re1673bb0bbfea21d6e5dba73e013b09b8b49b89b, Tue Jul 10 17:26:48 CST 2018
Took 0.0032 seconds
hbase(main):001:0>
创建普通表
创建普通表的语法为:create '表的名称','列族的名称'
> create 'cga_info','info'
Created table cga_info
Took 8.7301 seconds
=> Hbase::Table - cga_info
> list
TABLE
cga_info
1 row(s)
Took 0.0277 seconds
=> ["cga_info"]
创建 namespace
namespace 是一组表的逻辑划分
创建 namespace 的语法为:create_namespace '名称'
> create_namespace 'nn'
0 row(s)
Took 0.1280 seconds
在指定 namespace 下创建表
在指定 namespace 下创建表:create 'namespace的名称:表名','列族'
> create 'nn:student','info'
0 row(s)
Took 0.2680 seconds
=> Hbase::Table - nn:student
查看指定 namespace 下的表
查看指定 namespace 下的表:list_namespace_tables 'namespace 的名称'
> list_namespace_tables 'nn'
TABLE
student
1 row(s)
Took 0.3681 seconds
=> ["student"]
查看表结构
hbase(main):006:0> describe 'cga_info'
Table cga_info is ENABLED
cga_info
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_B
EHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false'
, DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATI
ON_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY
=> 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false'
, COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
1 row(s)
Took 0.1823 seconds
增加数据
增加数据:put '表的名称','RowKey','列族的名称列的名称','具体的赋值'
> put 'cga_info','123001','info:name','Kobe'
0 row(s)
Took 0.1580 seconds
> put 'cga_info','123001','info:gender','male'
0 row(s)
Took 0.0390 seconds
> put 'cga_info','123001','info:age','40'
0 row(s)
Took 0.0250 seconds
> put 'cga_info','123001','info:address','Los Angeles'
0 row(s)
Took 0.0170 seconds
get 方式查询数据
get 功能:精确查询。
精确查询某一个 RowKey 中存储的内容:get '表的名称','RowKey'
> get 'cga_info','123001'
COLUMN CELL
info:address timestamp=1523350574004, value=Los Angeles
info:age timestamp=1523350540131, value=40
info:gender timestamp=1523350499780, value=male
info:name timestamp=1523350443121, value=Kobe
1 row(s)
Took 0.0390 seconds
精确查询某一个 RowKey 中的一个单元格中存储的内容。
语法:get '表的名称','RowKey','列名'
> get 'cga_info','123001','info:name'
COLUMN CELL
info:name timestamp=1586826576549, value=Kobe
1 row(s)
Took 0.0069 seconds
scan 方式查询数据
scan 方式查询数据
> put 'cga_info','123002','info:name','Victoria'
> put 'cga_info','123002','info:gender','female'
> put 'cga_info','123002','info:age','40'
> put 'cga_info','123002','info:address','London'
> put 'cga_info','123003','info:name','Taylor'
> put 'cga_info','123003','info:gender','female'
> put 'cga_info','123003','info:age','30'
> put 'cga_info','123003','info:address','Redding'
> put 'cga_info','123004','info:name','LeBron'
> put 'cga_info','123004','info:gender','male'
> put 'cga_info','123004','info:age','33'
> put 'cga_info','123004','info:address','Cleveland'
scan 功能:在某个范围内查询
查询表中某个列族下所有列的信息:scan '表的名称',{Columns=>'列族'}
> scan 'cga_info',{COLUMNS=>'info'}
ROW COLUMN+CELL
123001 column=info:address, timestamp=1523350574004, value=Los Angeles
123001 column=info:age, timestamp=1523350540131, value=40
123001 column=info:gender, timestamp=1523350499780, value=male
123001 column=info:name, timestamp=1523350443121, value=Kobe
123002 column=info:address, timestamp=1523351932415, value=London
123002 column=info:age, timestamp=1523351887009, value=40
123002 column=info:gender, timestamp=1523351993106, value=female
123002 column=info:name, timestamp=1523351965188, value=Victoria
123003 column=info:address, timestamp=1523352194766, value=Redding
123003 column=info:age, timestamp=1523352108282, value=30
123003 column=info:gender, timestamp=1523352060912, value=female
123003 column=info:name, timestamp=1523352091677, value=Taylor
123004 column=info:address, timestamp=1523352217267, value=Cleveland
123004 column=info:age, timestamp=1523352229436, value=33
123004 column=info:gender, timestamp=1523352267416, value=male
123004 column=info:name, timestamp=1523352251926, value=LeBron
4 row(s)
Took 0.0480 seconds
查询表中具体的一个列中存储的信息。
语法:scan '表的名称',{Columns=>'列的具体名称'}
语法:scan ‘表的名称‘,{Columns=>’列的具体名称‘}
> scan 'cga_info',{COLUMNS=>'info:name'}
ROW COLUMN+CELL
123001 column=info:name, timestamp=1523350443121, value=Kobe
123002 column=info:name, timestamp=1523351965188, value=Victoria
123003 column=info:name, timestamp=1523352091677, value=Taylor
123004 column=info:name, timestamp=1523352251926, value=LeBron
4 row(s)
Took 0.0300 seconds
指定条件查询数据
查询 RowKey 从 123002 开始,然后读两行的数据:
> scan 'cga_info',{STARTROW=>'123002','LIMIT'=>2}
ROW COLUMN+CELL
123002 column=info:address, timestamp=1523351932415, value=London
123002 column=info:age, timestamp=1523351887009, value=40
123002 column=info:gender, timestamp=1523351993106, value=female
123002 column=info:name, timestamp=1523351965188, value=Victoria
123003 column=info:address, timestamp=1523352194766,value=Redding
123003 column=info:age, timestamp=1523352108282, value=30
123003 column=info:gender, timestamp=1523352060912, value=female
123003 column=info:name, timestamp=1523352091677, value=Taylor
2 row(s)
Took 0.0170 seconds
查询 RowKey 从 123001 开始,然后读取两行,读取列名称为name 的单元格中存储的信息:
> scan 'cga_info',{STARTROW=>'123001','LIMIT'=>2,COLUMNS=>'info:name'}
ROW COLUMN+CELL
123001 column=info:name, timestamp=1523350443121, value=Kobe
123002 column=info:name, timestamp=1523351965188, value=Victoria
2 row(s)
Took 0.0500 seconds