文章目录
HBase
RDBMS(关系型数据库–MySQL|Oracle)问题
1.建表限定列数量,无法动态随意扩展。
2.MySQL单表数据库500w左右,数量小。(单表上限)
3.MySQL单表数据量过大,横向切分。(手动开发、复杂、稳定性低、效率低)
4.MySQL中单表的列过多,影响表的CRUD效率。(MySQL单表列最好不要超过30列)
5.MySQL单表的列过多,纵向切分。(手动开发、复杂、稳定性)
6.MySQL数据库允许空值,空值的空间是占用了的(海量数据面前造成空间浪费)
7.MySQL数据库中每个单元格,只能保存一个值(一个版本)
总结:(mysql存在问题)
a.海量数据存储问题
b.实时检索问题
Hbase 数据库(海量存储、实时检索)
简介
HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储。
官方网站:http://hbase.apache.org
- 2006年Google发表BigTable白皮书
- 2006年开始开发HBase
- 2008年北京成功开奥运会,程序员默默地将HBase弄成了Hadoop的子项目
- 2010年HBase成为Apache顶级项目
- 现在很多公司基于HBase开发出了定制版,比如阿里云HBase
总结:
HBase是构建在HDFS之上的分布式、面向列的存储系统,在需要实时读写、随机访问的超大规模数据集的时候,可以使用HBase。
什么情况下需要HBase以及为什么需要HBase
1.一个表数据量会超过500w,未来数据量会达到百亿条。
2.实时查询。(秒级 毫秒级读写)
原因:
# 海量数据存储
一个表百亿行 百万列;(MySQL实战最大值500万行,30列)
# 实时查询
1秒内查询得到结果。
HBase特点
# 1. 容量大
HBase单表百亿行,百万列。
# 2. 面向列
HBase存储是面向列,可以再数据存在以后动态增加新列和数据,并支持列数据的独立操作。
# 3. 多版本
HBase每个数据,可以同时保存多个版本,按照时间去标记。
# 4. 稀疏性
HBase每条数据的增删,并不是要操作所有的列,列可以动态增加,可以存在大量空白单元格,不会占用磁盘空间,这对于海量数据来讲,非常重要。
# 5. 扩展性
底层使用HDFS,存储能力可以横向扩展。
# 6. 高可靠性
底层使用HDFS,拥有replication的数据高可靠性。
# 7. 高性能
表数据达到一定规模,"自动分区",具备主键索引,缓存机制,使得HBase海量数据查询能达到毫秒级。
HBase和RDBMS对比
HBase | 关系型数据库 |
---|---|
数据库以**region **的形式存在 | 数据库以Table的形式存在 |
使用**行键 **(row key) | 支持主键PK |
使用行表示一条数据 | 一条数据用row代表 |
使用列 column、列族 column family | column代表列数据的含义 |
使用HBase shell 命令操作数据 | 使用SQL操作数据 |
数据文件可以基于HDFS,是分布式文件系统, 可以任意扩展,数据总量取决于服务器数量 | 数据总量依赖于单体服务器的配置 |
不支持事务、不支持ACID | 支持事务和ACID |
不支持表连接 | 支持join表连接 |
HBase表逻辑结构
数据相关概念
# namespace 命名空间
管理多个表,相当于Database。
hbase管理表的结构,在HDFS中对应一个文件夹。
# table 表
存储管理数据,相当于table。
hbase管理数据的结构,在HDFS中对应一个文件。
# column
列,每个列对应一个单元格。
# column family 列族
包含多个列,一组拥有相关业务含义列组成1个列族。
表中数据的列,要属于某个列族,所有的列的访问格式(列族:列名)
# rowkey 主键
类似于id,用来唯一标识一条数据。
用来标记和检索数据的主键key。
# cell 单元格
由`row key+column family+column+version` 唯一确定的一条数据。
每个单元格可以保存值多个版本,按照时间排序,最新值在最前面。
# timestamp 时间戳
时间戳,每个单元格可以保存多个值,每个值有对应的时间戳,每个cell中,不同版本的数据倒叙排序,排在最前面的是最新数据。
HBase架构体系(物理结构)
1.主从架构
Zookeeper
1.保存HMaster管理表的元数据。
2.HMaster解决单点故障(两个HMaster--Zookeeper自动处理)
3.保存RegionServer从机状态信息。
HMaster(主)
1. 管理RegionServer及其状态。
2. 管理表的元数据。
3.接受DDL语句,建表、删除表、alter。
4.对HRegionServer做数据分布负载均衡。
HRegionServer(从)
1. 定时向HMaster报告自身节点状态信息。
2.保存表的数据部分。
3.接受并执行DML语句。
(添加-put、删除-delete、查询-get、scan)
没有修改,使用put覆盖代替修改;
delete删除,不会立刻操作数据,打标记。
4.维护横向切分后的数据。
HRegion
一段表的数据(横向拆分)。即start key~end key之间的数据一个Region(拆分后的小表数据)。
store
横向拆分后的子表的纵向拆分。
一个Region内部,每个列族数据,单独存放叫做store。
namespace--table--region--store(1对多)
架构相关概念
HRagionServer
HRegionServer(和DataNode同一节点)
1. 存储表数据部分
2. put delete get scan等针对数据的操作
3. 定时向Master报告自身节点的状态
4. 管理表的数据的Table的数据
HMaster
HMaster
1. Region Server状态的管理
2. 表的管理:create drop alter
3. 实现HRegionServer的数据负载均衡,平衡HRegion的分布
Zookeeper
Zookeeper
1. 解决HMaster的单点故障问题
2. 存放HMaster管理的HRegionServer的状态信息,并通知HMaster
3. 存放HMaster管理的表的元数据信息
表名、列名、key区间等。
HRegion
HRegion
表的横向切片的物理表现,大表的子表,有(startkey endkey),多行数据。
为了减小单表操作的大小,提高读写效率。
Store
Store
1. 表的纵向切分的物理表现,按照列族作为切分。
2. 按照列族查询,仅需要检索一定范围内的数据,减少全表扫描。
HBase单机版安装
下载
地址:https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/
准备
- 安装并配置hadoop
[root@hbase40 installs]# jps 3440 Jps 3329 SecondaryNameNode 3030 NameNode 3134 DataNode
- 安装并配置单机zookeeper
[root@hbase40 installs]# jps 3329 SecondaryNameNode 3509 QuorumPeerMain 3030 NameNode 3595 Jps 3134 DataNode [root@hbase40 installs]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /opt/installs/zookeeper3.4.14/bin/../conf/zoo.cfg Mode: standalone
- 设置好日期同步
# 确保HBase主机的时间和网络时间一致 # 查看linux系统时间 [root@hbase40 installs]# date # 重启chronyd服务,同步系统时间。 [root@hbase40 installs]# systemctl restart chronyd [root@hbase40 installs]# date 2020年 04月 12日 星期日 22:51:31 CST
安装
# 1. 安装hbase
1. 解压HBase
[root@hbase40 modules]# tar zxvf hbase-1.5.0-bin.tar.gz -C /opt/install/
2. 配置环境变量
#JAVA
export JAVA_HOME=/opt/install/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
# HADOOP
export HADOOP_HOME=/opt/install/hadoop2.9.2/
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# zookeeper
export PATH=$PATH:/opt/install/zookeeper3.4.14/bin/
# HBase
export HBASE_HOME=/opt/install/hbase1.5
export PATH=$PATH:$HBASE_HOME/bin
3. 加载profile配置
source /etc/profile
# 2. 初始化配置文件
hbase-env.sh
hbase-site.xml
regioniservers
# 1 -------------------hbase-env.sh--------------------
# 配置Java_home
export JAVA_HOME=/opt/installs/jdk1.8
# 注释掉如下2行。
# export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m"
# export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m"
# 禁用内置zookeeper
export HBASE_MANAGES_ZK=false
# 2. -------------------hbase-site.xml-------------------------
<configuration>
<!-- hbase的访问hdfs入口,ns HaHadoop的虚拟命名空间 -->
<property>
<name>hbase.rootdir</name>
<value>hdfs://hbase40:9000/hbase</value>
</property>
<!-- 使用分布式模式 -->
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- zookeeper集群地址,端口默认2181不需要指定 -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>hbase40</value>
</property>
<!--zookeeper的默认工作目录,就是data目录,要和zookeeper的一样-->
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/install/zookeeper3.4/data/</value>
</property>
<!--配置hdfs的hdfs的flush方式:否则该版本启动会报错-->
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
</configuration>
# -------------------配置regionservers(regionserver所在节点的ip) -------------------
hadoop30
# 3. 启动hbase
# 启动顺序
zookeepr->hdfs->hbase
# 1. 启动HMaster
[root@hbase40 install]# hbase-daemon.sh start master
# 关闭
[root@hbase40 install]# hbase-daemon.sh stop master
# 2. 启动HRegionServer
[root@hbase40 install]# hbase-daemon.sh start regionserver
# 关闭
[root@hbase40 install]# hbase-daemon.sh stop master
# 3.全部启动
[root@hbase40 install]# start-hbase.sh
# 4. 验证访问
1. java进程查看
[root@hadoop30 installs]# jps
4688 NameNode
5618 HMaster
5730 HRegionServer
4819 DataNode
3509 QuorumPeerMain
6150 Jps
4984 SecondaryNameNode
2. HMaster WebUI查看
http://ip:16010
3. 进入客户端
hbase shell
hbase(main):001:0>
HBase 命令
注意:命令行中,删除字符用
ctrl+backspace
HBase shell 命令结尾没有“分号”
1. 客户端进出命令
# 进入客户端:
./hbase shell
# 退出客户端命令:
quit/exit
# 帮助
help
2. namespace的管理命令(相当于数据库中DataBase)
默认存在一个default的namespace
#1. 查看namespace
list_namespace
#2. 创建namespace
create_namespace "命名空间名字"`
#3. 删除namespace
drop_namespace "命令空间名字"
3. 表的操作(table)
# 1. 查看所有表
hbase(main):008:0> list (目前没有表)
TABLE
0 row(s) in 0.0320 seconds
=> []
hbase(main):009:0>
# 2. 查看某个namespace下的所有表
hbase(main):001:0> list_namespace_tables "test"
TABLE
user
1 row(s) in 0.2980 seconds
# 3. 创建表 (如果不指定namespace,则放在默认default下)
语法:create "test:user","info","edu"
hbase(main):004:0> create "test:user","info","edu"
0 row(s) in 2.5580 seconds
=> Hbase::Table - test:user
# 4. 查看表结构(表名、列族相关信息)
语法:desc "namespace:表名字"
hbase(main):002:0> desc "test:user"
Table test:user is ENABLED
test:user
COLUMN FAMILIES DESCRIPTION
{NAME => 'edu', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE',
DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => '
true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
{NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE',
DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE =>
'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
2 row(s) in 30.3330 seconds
hbase(main):003:0>
# 5. 删除表和禁用表(HBase对表进行了加锁,若想删除必须先进行禁用)
语法:disable "namespace:表名字"
hbase(main):010:0> disable "cyf:user"
0 row(s) in 2.2970 seconds
hbase(main):011:0>
语法:drop "namespace:表名"
hbase(main):011:0> drop "cyf:user"
0 row(s) in 1.3540 seconds
hbase(main):012:0>
4.表数据操作(增删改查)
1.HBase添加数据,会按照rowkey进行排序(默认字典排序)。
2.HBase查询数据,查询结果按照rowkey排序展示。
# 1. 添加数据(每次只能添加一个列,一个put代表一个数据)
# 表 rowkey 列族:列名 值
语法:put "namespace:表","rowkey","列族1:列名1","值"
put "test:user","1001","info:name","cyf"
put "test:user","1001","info:age","66"
put "test:user","1001","info:mobile","110000"
# 2. 根据rowkey查找数据(get)
语法:get "namespace:表名","rowkey"
get "test:user","1001"
hbase(main):006:0> get "test:user","1001"
COLUMN CELL
info:age timestamp=1624148527287, value=66
info:mobile timestamp=1624148627742, value=110000
info:name timestamp=1624148314733, value=lihao
1 row(s) in 0.0130 seconds
hbase(main):007:0>
# 3. 根据rowkey和列族查找部分列
语法:get "namespace:表名","rowkey","列族:列"
get "test:user","1001","info:name"
hbase(main):001:0> get "test:user","1001","info:name"
COLUMN CELL
info:name timestamp=1624148314733, value=lihao
1 row(s) in 30.3270 seconds
hbase(main):002:0>
# 4. scan 查询表中所有数据(scan 多行查询)
语法:scan "namespace:表名"
scan "test:user"
hbase(main):006:0> scan "test:user"
ROW COLUMN+CELL
1001 column=info:age, timestamp=1624148527287, value=66
1001 column=info:mobile, timestamp=1624148627742, value=110000
1001 column=info:name, timestamp=1624148314733, value=lihao
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624149028019, value=cyf
1003 column=info:age, timestamp=1624149122537, value=15
1003 column=info:name, timestamp=1624149045376, value=fjl
3 row(s) in 0.0640 seconds
hbase(main):007:0>
# 5.查询表中前2条数据(scan)
语法:scan "namespace:表名",{LIMIT=>2}
scan "test:user",{LIMIT=>2}
# LIMIT注意事项:
LIMIT必须大写
等号:=>
hbase(main):007:0> scan "test:user",{LIMIT=>2}
ROW COLUMN+CELL
1001 column=info:age, timestamp=1624148527287, value=66
1001 column=info:mobile, timestamp=1624148627742, value=110000
1001 column=info:name, timestamp=1624148314733, value=lihao
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624149028019, value=cyf
2 row(s) in 0.0350 seconds
hbase(main):008:0>
# 6. 使用start row 和 end row 范围查找(1002~1003前闭后开)
语法:scan "namespace:表名",{STARTROW=>"",ENDROW=""}
scan "test:user",{STARTROW=>"1002",ENDROW=>"1004"}
hbase(main):013:0> scan "test:user",{STARTROW=>"1002",ENDROW=>"1004"}
ROW COLUMN+CELL
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624149028019, value=cyf
1003 column=info:age, timestamp=1624149122537, value=15
1003 column=info:name, timestamp=1624149045376, value=fjl
2 row(s) in 0.0270 seconds
hbase(main):014:0>
# 7. 查询rowkey=“1001”并且向后查询三条,使用start row和limit查找
语法:scan "namespace:表",{START=>"10001",LIMIT=>3}
scan "test:user",{STARTROW=>"10001",LIMIT=>2}
hbase(main):014:0> scan "test:user",{STARTROW=>"10001",LIMIT=>2}
ROW COLUMN+CELL
1001 column=info:age, timestamp=1624148527287, value=66
1001 column=info:mobile, timestamp=1624148627742, value=110000
1001 column=info:name, timestamp=1624148314733, value=lihao
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624149028019, value=cyf
2 row(s) in 0.0280 seconds
hbase(main):015:0>
# 8. 修改数据(本质上是覆盖--put)
语法:put "namespace:表","rowkey","列族:列名","值"
put "test:user","1001","info:name","yf"
hbase(main):005:0> put "test:user","1002","info:name","yf"
0 row(s) in 0.0110 seconds
hbase(main):006:0> scan "test:user"
ROW COLUMN+CELL
1001 column=info:age, timestamp=1624148527287, value=66
1001 column=info:mobile, timestamp=1624148627742, value=110000
1001 column=info:name, timestamp=1624157416165, value=lihao
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624157430474, value=yf
1003 column=info:age, timestamp=1624149122537, value=15
1003 column=info:name, timestamp=1624149045376, value=fjl
3 row(s) in 0.0170 seconds
hbase(main):007:0>
# 9. 删除数据(删除某个cell)
语法:delete "namespace:表","rowkey","列族:列名"
delete "test:user","1001","info:mobile"
hbase(main):007:0> delete "test:user","1001","info:mobile"
0 row(s) in 0.0370 seconds
hbase(main):008:0> scan "test:user"
ROW COLUMN+CELL
1001 column=info:age, timestamp=1624148527287, value=66
1001 column=info:name, timestamp=1624157416165, value=lihao
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624157430474, value=yf
1003 column=info:age, timestamp=1624149122537, value=15
1003 column=info:name, timestamp=1624149045376, value=fjl
3 row(s) in 0.0250 seconds
hbase(main):009:0>
# 10. 删除某个rowkey对应的整行数据(deleteall)
语法:deleteall "namespace:表","rowkey"
deleteall "test:user","1001"
hbase(main):009:0> deleteall "test:user","1001"
0 row(s) in 0.0170 seconds
hbase(main):010:0> scan "test:user"
ROW COLUMN+CELL
1002 column=info:age, timestamp=1624149095675, value=26
1002 column=info:name, timestamp=1624157430474, value=yf
1003 column=info:age, timestamp=1624149122537, value=15
1003 column=info:name, timestamp=1624149045376, value=fjl
2 row(s) in 0.0110 seconds
hbase(main):011:0>
# 11. 统计表中所有数据
# HBase没有外键,也没有表连接
语法:count "namespace:表"
count "test:user"
hbase(main):011:0> count "test:user"
2 row(s) in 0.0360 seconds
=> 2
hbase(main):012:0>
5. 多版本问题
# 修改表的指定列族中数据版本数量。
# alter "namespace:表",{NAME=>"info",VERSIONS=>2}
# alter "test:user",{NAME=>"info",VERSIONS=>2}
hbase(main):012:0> alter "test:user",{NAME=>"info",VERSIONS=>2}
Updating all regions with the new schema...
0/1 regions updated.
1/1 regions updated.
Done.
0 row(s) in 3.6380 seconds
hbase(main):013:0> desc "test:user"
Table test:user is ENABLED
test:user
COLUMN FAMILIES DESCRIPTION
{NAME => 'edu', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE',
DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => '
true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
{NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '2', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE',
DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE =>
'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
2 row(s) in 0.0410 seconds
hbase(main):014:0>
# 同一个cell添加2次数据。
hbase(main):014:0> put "test:user","1002","info:name","aaa"
0 row(s) in 0.2620 seconds
hbase(main):015:0>put "test:user","1002","info:name","bbb"
0 row(s) in 0.0290 seconds
# 查看多版本
hbase(main):016:0> get "test:user","1002",{COLUMN=>'info:name',VERSIONS=>2}
COLUMN CELL
info:name timestamp=1624158383906, value=bbb
info:name timestamp=1624158364824, value=aaa
1 row(s) in 0.0360 seconds
hbase(main):017:0>
# 表的列族的VERSIONS=>2表示的该列族的数据,要保存2个版本。如果put3次,则保留最新的版本。
# 使用get和scan查询,默认查询到的只有最新版本数据
# 查询多个版本 get "namespace:表","rowkey",{COLUMN=>"列族:列",VERSIONS=>数字}
HBase API
API介绍
HBase Java API
1.Configuration:访问HBase配置信息。
2.Configuration:hbase数据库连接,操作表的数据。
3.Admin:HBase客户端连接,操作表结构 namespace
HBase 表示数据(数据查询 Delete 表 namespace)模型的API
1.NamespaceDescriptor 表示namespace命名空间。
API | 含义 | 创建 |
---|---|---|
Configuration | 配置文件 | HBaseConfiguration.create(); |
Connection | 连接,用来操作数据 | ConnectionFactory.createConnection(conf); |
Admin | 客户端,用来操作元数据 (namespace和table结构) | conn.getAdmin(); |
NamespaceDescriptor | 命名空间相当于database | NamespaceDescriptor.create(“btrc”).build(); |
TableName | 表名 | TableName.valueOf(“btrc:user”); |
HTableDescriptor | 表 | new HTableDescriptor(tablename); |
HColumnDescriptor | 列族 | new HColumnDescriptor(“info”); |
Put | 添加数据 | new Put(Bytes.toBytes(“1001”)); |
Delete | rowkey的删除条件 | new Delete(Bytes.toBytes(“1001”)); |
Get | scan单行查询器 | new Get(Bytes.toBytes(“1019”)); |
Scan | scan多行查询器 | new Scan(); |
Result | 查询结果集(单条结果) | table.get(get); |
ResultScanner | 查询结果集(N条结果) | table.getScanner(scan); |
环境准备
-
依赖
<properties> <!--定义版本号--> <hbase.version>1.5.0</hbase.version> <java.version>1.8</java.version> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-common</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-protocol</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-server</artifactId> <version>${hbase.version}</version> </dependency>
-
HBase客户端连接编码步骤
//将hbase中的conf中的 **hbase-site.xml**放到resource配置文件目录中。
public void test2() throws Exception{
//1.初始化配置
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","192.168.153.40");
//2.创建Hbase连接
Connection conn = ConnectionFactory.createConnection(conf);
//3.获得操作表和namespace结构的admin
Admin admin = conn.getAdmin();
//4.java中创建namespace对象
NamespaceDescriptor btrc = NamespaceDescriptor.create("btrc").build();
//5.执行创建namespace操作
admin.createNamespace(btrc);
//释放资源
admin.close();
}
常用API
表相关的API
creat "namespace:表",”列族“
表名字:TableName
TableName.valueOf("namespace:”表名")
表:HTableDesscriptor
new HTableDesscriptor(”table“);
列族:HColumnDescriptor
new HColumnDescriptor("列族名字");
创建表:
①获得表名字
②创建Table
③创建列族
④将列族,装配在table中
⑤创建表:admin
1. 创建namespace和表
# 创建namespace
//1. 构建namespace信息。
NamespaceDescriptor btrc = NamespaceDescriptor.create("btrc").build();
//2. 创建namespace
admin.createNamespace(btrc);
# 创建表
public void test3() throws Exception{
//1.初始化配置文件
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","192.168.153.40");
//启动显示日志
BasicConfigurator.configure();
//2.获得hbase连接
Connection conn = ConnectionFactory.createConnection(conf);
//3.获得admin
Admin admin = conn.getAdmin();
//1.创建表名
TableName tableName = TableName.valueOf("btrc:persons");
//2.构造表
HTableDescriptor table = new HTableDescriptor(tableName);
//3.构造列族
HColumnDescriptor info = new HColumnDescriptor("info");
HColumnDescriptor edu = new HColumnDescriptor("edu");
//4.将列族装配在table中
table.addFamily(info);
table.addFamily(edu);
//5.admin创建表
admin.createTable(table);
admin.close();
}
2. 表操作
-
判断表是否存在
# 操作表,使用admin //1. 创建表名 TableName tableName = TableName.valueOf("btrc:persons"); //2. 判断表是否存在 boolean b = admin.tableExists(tableName); System.out.println(b?"存在":"不存在");
3. 添加
# 操作数据使用conn
**Bytes是HBase提供的进行字节和java数据类型转化的工具类
public void test2() throws Exception{
//0.获得连接
Connection conn = HBaseUtil.getConnection();
//1.获得table表
TableName tableName = TableName.valueOf("btrc:persons");
Table table = conn.getTable(tableName);
//2.创建一个put
Put put = new Put(Bytes.toBytes("1001"));
//3.向put中绑定添加的数据(列族:列,“值”)
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("liqinghua"));
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"),Bytes.toBytes("24"));
//4.使用table,添加put的数据---将put放入table中
table.put(put);
table.close();
}
4. 修改
public void test5() throws Exception{
//0.获得连接
Connection conn = HBaseUtil.getConnection();
//1.获得table表
TableName tableName = TableName.valueOf("btrc:persons");
Table table = conn.getTable(tableName);
//2.创建一个put
Put put = new Put(Bytes.toBytes("1001"));
//3.向put中绑定添加的数据(列族:列,“值”)修改实质即为重新添加
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("wangyuxi"));
//4.使用table,添加put的数据---即修改数据
table.put(put);
table.close();
}
5. 删除
public void test3() throws Exception{
//0.获得连接
Connection conn = HBaseUtil.getConnection();
//1.先获得table
Table table = conn.getTable(TableName.valueOf("btrc:persons"));
//3.创建删除Delete
Delete delete = new Delete(Bytes.toBytes("10012"));
//4.调用table.delete(delete)----执行删除
table.delete(delete);
}
6. 查询
-
根据rowkey单条查询(get)。
public void test6() throws Exception{ //1.获得连接 Connection conn = HBaseUtil.getConnection(); //2.获得表table TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //3.封装查询操作Get(rowkey) Get get = new Get(Bytes.toBytes("1008")); //4.执行查询,获得查询结果Result Result result = table.get(get); //5.处理结果Result info-name,info-age byte[] names = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")); byte[] ages = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); //将字节name转换String name //将字节name转换Int age String name = Bytes.toString(names); int age = Bytes.toInt(ages); System.out.println("name"+name); System.out.println("age"+age); }
-
多条查询(scan-rowkey=1001,查询4条)
scan "btrc:persons",{STARTROW=>"1001",LIMIT=>4} public void test7() throws Exception{ //1.获得连接 Connection conn = HBaseUtil.getConnection(); //2.获得表table TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //3.创建Scan多行查询扫描器 Scan scan = new Scan(); //4.设置Scan扫描器(列族,起始rowkey,limit) scan.addFamily(Bytes.toBytes("info")); scan.withStartRow(Bytes.toBytes("1001")); scan.setLimit(4); //5.执行扫描--table.getScanner(scan)得到ResultScanner ResultScanner scanner = table.getScanner(scan); //6.处理结果,遍历ResultScanner得到数据 for (Result result : scanner) { byte[] namebyt = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")); byte[] agebyt = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); String name = Bytes.toString(namebyt); int age = Bytes.toInt(agebyt); System.out.println(name+age); } table.close(); }
HBase架构原理
HBase的Region分区的意义
# HBase的Region分区的意义
HRegion:
1.HBase中数据存在RegionServer管理的Region中。
2.一旦Region数据量达标到一定规模,拆分。()
3.每个region(startkey---endkey)
Meta表(元数据表)
1.表 rowkey区间范围,每条信息范围对应一个Region,对应一个Region所在RegionServer机器。
方便客户端(添加 查询)快速定位到Region。
读写数据操作原理
读数据
# 客户端查询数据执行原理
1.客户端访问zookeeper:(目的:获得meta表的位置)
HMaster地址
HRegionServer地址
hbase:meta表数据所在regionServer地址
2.查询meta所在机器,确定rowkey和user对应的Region在哪个RegionServer
3.请求Region所在RegionServer,数据检索
4.获得查询结果。
写数据
# 客户端写入数据执行原理
1.请求zookeeper
获得meta表位置。
2.请求meta所在RegionServer节点确定表,rowkey并返回对应region所在的RegionServer服务节点。
3.请求RegionServer向里面写入数据。
**注意
①将写操作记录在日志(HLog、WAL:write ahead log)
②regionserver接受数据写入内存中。写入数据的内存:Memstore。
HBase底层原理
Region split 分区
# Region split的原因
1.随着put操作,Region空间不断增加,导致put delete get需要定位rowkey的时间逐渐增加。(效率降低)
# Region split 分区
2.HBase,当Region达到一定条件的时候,会对Region进行切分,并且将切分后的Region交给不同的RegionServer管理。
a.根据rowkey在region定位数据,速度变快
b.Region数据在RegionServer分布管理负载均衡。(合理利用硬件资源)
# Region split 分区时机
3.Region Split的时机
hbase.hregion.memstore.flush.size=128M
分区时机:
Region数量(平方)*hbase.hregion.memstore.flush.size=Region分区时的大小
Region分区时机:
128M --- 512M --- 1152M---....10GB---10GB---10GB
# 默认Region split 方案的缺点
由于HBase的默认分区没有考虑实际业务场景中rowkey规律:
a.拆分后的Region的后续增长极其不均衡,导致数据倾斜。
b.访问Region中的数据,访问的概率也不同,访问负载不均衡,数据热点问题。
Region Split 分区
-
原因(为啥)
提高Region的负载和读写效率。
-
说明
Region一拆为二(平均)
-
默认分区机制
Region中数据超过128M、512M、1152M… *Region数量2hbase.hregion.memstore.flush.size … 10G、10G
查看参数
hbase.hregion.memstore.flush.size=128M hbase.hregion.max.filesize=10G
-
问题
默认分区容易导致数据倾斜,硬件资源无法充分利用。
Region切片预分区
预分区即创建表的时候,决定分区,Region的个数,每个Region维护的rowkey区间。
目的:防止数据热点,防止数据倾斜。(均衡分布数据到不同Regionc)
rowkey设计核心操作。
-
为什么
- 增加读写效率。(多个region分布在不同的RegionServer中,可以提高并发效率)
- 尽量保证每个Region中的数据量相当,防止数据倾斜。(合理利用计算资源)
-
分区的效果
每个Region维护一对StartKey和EndKey,限定维护输入rowkey范围。
添加数据时,讲rowkey放入匹配的region中。
-
创建表时分区,手动指定(分区方案)
命令:
create "namespace:表","列族",SPLITS=>["100000","200000","300000","400000"]
总结:按照splits中指定的分区key,分成4个区。
效果:
从文件中手动指定。
- 创建分区文件,输入分区key信息。
分区键的文件:(splits.txt)
10000
20000
30000
- 使用文件创建分区
create “btcr:t_users1”,“info”,SPLITS_FILE => “/opt/install/hbase1.5/splits.txt”
java代码分区:
admin.createTable(htable,splitkeys);
RowKey设计
-
RowKey作用
- 读写数据时通过 RowKey 找到对应的 Region;
- Region、Store、Store File中的数据,会按照rowkey的字典顺序排序。
- 通过 get 方式,指定 RowKey 获取唯一一条记录
- 通过 scan 方式,设置 startRow 和 stopRow 参数进行范围匹配
-
设计原则
- 唯一,不能重复
- Region负载均衡,散列,后添加的数据,总是能均匀的散部在不同的Region中。
- 避免热点数据,避免访问量频繁的数据,分布在同一个或者两个Region中。
- 满足业务查询需求。
-
技巧
- 进行查询的时候,根据RowKey从前向后匹配
- 结合业务场景特点,选择合适的字段来做为RowKey,并且按照查询频次来放置字段顺序
- 宏观散列,局部排序。
-
案例
1. 查询某用户在某应用中的操作记录 hash(userid) + appid + timestamp 2. 查询某用户在某应用中的操作记录(优先展现最近的数据) hash(userid) + appid + (Long.Max_Value - timestamp) 3. 查询某用户在某段时间内所有应用的操作记录 hash(userid) + timestamp + appid 4. 查询某用户的基本信息 hash(userid)
MemStore Flush刷写
1.client不断向RegionServer维护的Region中添加数据。
Region维护的数据存在于内存中。
client向HBase写入数据,Memstore。
说明:Region维护在内存中,目的:写入数据和删除块。
2.问题:
Region数据量过大,RegionServer内存不足。
3.解决问题:
HBase监控,当Region中数据达到条件值,将Region中Store,分别向HDFS进行刷写flush,将数据写入到HDFS文件中。
列族数量==store数量:每个store对应hdfs中的一个文件夹。
结论:每次达到刷写条件,就将store数据flush到hdfs文件中,随着数据增长,会不断刷写文件。(有很多小的store file)
4.flush时机
a.Region Server整个服务总内存占用达到40%,执行全局刷写:(全局flush:影响客户端操作,该flush会暂停客户端读写操作)
hbase.regionserver.global.memstore.size
b.当MemStore持续写入1h,触发局部刷写:
hbase.regionserver.optionalcacheflushinterval
c.当单个Region中的数据文件大小超过128M(局部刷写):
hbase.hregion.memstore.flush.size
b和c只要达到一个条件就进行刷写。
d.手动flush
flush "namespace:表名"
HBase刷写到hdfs的存储位置:http://192.168.153.40:50070/explorer.html#/hbase/data/btrc/persons/19a0c4f38481013611b4102c32a1c761/info
-
说明
简言:持久化,保护数据不丢失。
将RegionServer中内存中的数据Memstore,写入到硬盘中。
-
图
-
时机
- Region Server整个服务总内存占用达到40%该flush会暂停客户端读写操作
对应参数:hbase.regionserver.global.memstore.size
- MemStore持续写入1h。
对应参数:hbase.regionserver.optionalcacheflushinterval
- 单个Region中的数据文件大小超过128M。
对应参数:hbase.hregion.memstore.flush.size
-
手动flush
命令:
flush "namespace:表名"
Store File Compact合并
1.问题:
随着HBase运行,HBase基于HDFS中,会不断的刷写成小的store file文件(几十MB)
总结:大量刷写次数,产生大量的小的刷写文件store file,查询storefile中的数据,需要将region所管理所有storefile遍历才能查到?
问题:大量的小storefile在内存中,遍历所有storefile查询效率。
a.小文件多
b.小文件整体没有顺序。
c.HDFS不适合管理小文件,文件管理效率也会降低。
2.解决方案:
store File compaction 合并
-
原因
storefile小文件过多,查询时,需要遍历所有文件,效率低。
storefile中遍布过期数据,占用空间,且查询效率低。
-
说明
简言:为提高检索效率,合并store
-
图
-
分类和时机
- (局部合并) minor compact
将部分相邻的store合并中等文件。
特点:少量相邻文件的合并。 发生频率较高,不影响性能。
手动命令:
compact "namespace:表名"
- (全局合并) majorcompact
将某个表:region:列族:所有storefile进行全局合并,产生一个文件。
特点: 1. 全局的所有store file文件的合并(数据量大)。 2. 去除删除被覆盖的文件。(删除过期版本的数据、delete命令删除过的数据起始还存在着,只不过打了标记) **删除过期数据(Region内存中 小Storefile中数据不会真正删除) 3. 特别消耗RegionServer的性能资源。(重点) 时机:每7天执行一次:参数:hbase.hregion.majorcompaction
一般手动触发。
手动触发命令:
major_compact "namespace:表名"
HBase为何能做到海量数据的实时读写(简单说明秒级原因)
# 1.Mestore
Region内存中
特点:
(内存)
(数据最新的)
(有序)
# 2.BlockCache(LRU)
HBase缓存中。
缓存策略:LRU(数据淘汰机制):保留最近最新使用多的数据。
# 3.磁盘storeFile(每个小file中rowkey是有序的)
磁盘的检索速度慢是因为寻道。
# 4.磁盘合并大storeFile(减少file数量,可以提高磁盘的检索效率)
1.磁盘storeFile文件数量少,减少遍历。
2.文件内以及文件在磁盘中,rowkey有序,代码检索还是磁盘寻道大大节省了时间
HBase之Mapreduce
Mapreduce思想
环境搭建
<!--hadoop依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!--hdfs依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!--mapreduce依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- hbase依赖 -->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-common</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-protocol</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>${hbase.version}</version>
</dependency>
编码实现
需求:
统计
btrc:persons
表中所有人的平均年龄?
package demo3;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.junit.Test;
import util.HBaseUtil;
import java.io.IOException;
public class AgeUserJob {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//job组装和启动代码
//初始化hdfs入口换成为hbase入口
Configuration conf = HBaseConfiguration.create();
conf.addResource("/hbase-site.xml");
//创建job
Job job = Job.getInstance(conf);
job.setJarByClass(AgeUserJob.class);
//配置map端
//TableInputformat
job.setInputFormatClass(TableInputFormat.class);
TableMapReduceUtil.initTableMapperJob("btrc:persons",new Scan(),UserMapper.class,Text.class,IntWritable.class,job);
//配置reduce端
//TableOutputFormat
job.setOutputFormatClass(TableOutputFormat.class);
//hbase的表必须创建
TableMapReduceUtil.initTableReducerJob("btrc:pavgage",UserReducer.class,job);
//启动job
boolean b = job.waitForCompletion(true);
System.out.println(b);
}
//map的类
public static class UserMapper extends TableMapper<Text, IntWritable>{
@Override
protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
//key -rowkey
//行数据 resulit
byte[] value1 = value.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
//得到age属性数据
int age = Bytes.toInt(value1);
//将age作为value输出
//将"age"作为key 输出,
context.write(new Text("age"),new IntWritable(age));
}
}
//reducer的类
public static class UserReducer extends TableReducer<Text,IntWritable, NullWritable>{
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//key:"age"
//values:[15,18,57]
int sum = 0;
int count = 0;
for (IntWritable value : values) {
sum+=value.get();
count++;
}
//sum总年龄 count 条数
int ave = sum/count;
//输出到HBase表
//封装成put
String rowkey = "23点07分_ave";
Put put = new Put(Bytes.toBytes(rowkey));
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("avgage"),Bytes.toBytes(ave));
/**
* context.write(xxx,put); put是一条数据,会被添加在HBase中
*/
context.write(NullWritable.get(),put);
}
}
}
Mapper
/**
* 泛型1:Map输出的key
* 泛型2:Map输出的value
*/
public class PersonMapper extends TableMapper<Text, LongWritable> {
/**
*
* @param key 输入的数据的rowkey
* @param value rowkey对应的一条结果
* @param context map的输出
*/
@Override
protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
//1. 获得value中的age
byte[] agebyte = value.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
if (agebyte != null) {
//2. 将age的数字输出
// 注意 输出的key,必须一样,这样shuffle才能分组成一组数据,方便reduce进行汇总。
context.write(new Text("age"), new LongWritable(Bytes.toInt(agebyte)));
}
}
}
Reducer
/**
* 输出的结果要进入HBase,所以输出的value必须是Put
*/
public class PersonReducer extends TableReducer<Text, LongWritable, NullWritable> {
/**
*
* @param key map输出的key输入到reduce
* @param values map输出的value输入到reduce
* @param context 写入到hbase表中的context
*/
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
// 1. 获得map输出的value,进行遍历,计算平均值。
int count = 0;
double sum = 0.0;
for (LongWritable p:values){
count++;
sum+=p.get();
}
double avgAge = sum/count;
//2. 创建
Put put = new Put(Bytes.toBytes("ageAge"));
//3. 将统计数据封装到Put中。
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("avg_age"), Bytes.toBytes(avgAge));
//4. 以null为key,put为value输出到HBase中。
context.write(NullWritable.get(),put);
}
}
Job
//1. 初始化配置文件,方便job操作HBase中的数据。(输入输出)
Configuration conf = HBaseConfiguration.create();
conf.addResource("/hbase-site.xml");
//2. 创建job任务
Job job = Job.getInstance(conf);
//3. 设置输入输出工具,HBase提供了从操作数据的InputFormat和OutputFormat
job.setInputFormatClass(TableInputFormat.class);
job.setOutputFormatClass(TableOutputFormat.class);
//4. 绑定Map相关信息:读入表,Scan,使用的Mapper,输出key类型,输出Value类型,job任务。
TableMapReduceUtil.initTableMapperJob("baizhins:person", new Scan(), PersonMapper.class, Text.class, LongWritable.class, job);
//5. 绑定reduce信息:输出信息表,reduce类型,job任务。
TableMapReduceUtil.initTableReducerJob("baizhins:person_avgAge", PersonReducer.class, job);
//6. 启动任务
job.waitForCompletion(true);
HBase架构完整版
集群规划
192.168.153.30: HMaster
192.168.153.31: HRegionServer
192.168.153.32: HRegionServer
192.168.153.33: HRegionServer
# 0 确保HDFS HA已经搭建完毕
[root@hadoop130 ~]# jps
1259 JournalNode
1965 NameNode
1758 DFSZKFailoverController
2110 Jps
1215 QuorumPeerMain
# 1. 安装HBase
1. 解压HBase
[root@hadoop30 modules]# tar zxvf hbase-1.5.0-bin.tar.gz -C /opt/install/
2. 配置环境变量
#JAVA
export JAVA_HOME=/opt/installs/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
# HADOOP
export HADOOP_HOME=/opt/installs/hadoop2.9.2/
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# zookeeper
export PATH=$PATH:/opt/installs/zookeeper3.4.14/bin/
# HBase
export HBASE_HOME=/opt/install/hbase1.5
export PATH=$PATH:$HBASE_HOME/bin
3. 加载profile配置
source /etc/profile
# 2. 初始化HBase 配置文件
# 1 -------------------hbase-env.sh--------------------
# 配置Java_home
export JAVA_HOME=/opt/install/jdk1.8
# 注释掉如下2行。
# export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m"
# export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m"
# 禁用内置zookeeper
export HBASE_MANAGES_ZK=false
# 2. -------------------hbase-site.xml-------------------------
<configuration>
<!-- hbase的入口,ns HaHadoop的虚拟命名空间 -->
<property>
<name>hbase.rootdir</name>
<value>hdfs://ns/hbase</value>
</property>
<!-- 使用分布式模式 -->
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- zookeeper集群地址,端口默认2181不需要指定 -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop30,hadoop31,hadoop32</value>
</property>
<!--zookeeper的默认工作目录,就是data目录,要和zookeeper的一样-->
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/install/zookeeper3.4.14/data</value>
</property>
<!--配置hdfs的hflush:否则该版本启动会报错-->
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
</configuration>
# 3. -------------------regionservers--------------------
hadoop31
hadoop32
hadoop33
# 3. 远程拷贝
1. hbase加入hadoop配置文件
[root@hadoop30 install]# ln -s /opt/install/hadoop2.9.2/etc/hadoop/core-site.xml /opt/install/hbase1.5/conf/core-site.xml
[root@hadoop30 install]# ln -s /opt/install/hadoop2.9.2/etc/hadoop/hdfs-site.xml /opt/install/hbase1.5/conf/hdfs-site.xml
2. 拷贝profile文件
[root@hadoop30 install]# scp /etc/profile root@hadoop31:/etc/
[root@hadoop30 install]# scp /etc/profile root@hadoop32:/etc/
[root@hadoop30 install]# scp /etc/profile root@hadoop33:/etc/
3. 拷贝hbase安装软件和配置文件
[root@hadoop30 install]# scp -r hbase1.5/ root@hadoop31:/opt/install/
[root@hadoop30 install]# scp -r hbase1.5/ root@hadoop32:/opt/install/
[root@hadoop30 install]# scp -r hbase1.5/ root@hadoop33:/opt/install/
4. 重新加载profile
[root@hadoop31 ~]# source /etc/profile
[root@hadoop32 ~]# source /etc/profile
[root@hadoop33 ~]# source /etc/profile
# 4. 启动HBase
# 启动HMaster
root@hadoop30 install]# hbase-daemon.sh start master
# 启动HRegionServer
[root@hadoop31 ~]# hbase-daemon.sh start regionserver
[root@hadoop32 ~]# hbase-daemon.sh start regionserver
[root@hadoop33 ~]# hbase-daemon.sh start regionserver
# 启动后需要等待20s左右,HMaster需要初始化工作。