HBase

一、概述

HBase(Hadoop Database),是一个基于Google BigTable论文设计的高可靠性、高性能、可伸缩的分布式存储系统。

1.1 CAP

CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

1.2 列存储(重点)

这个概念要从RDBMS说起,RDBMS操作的最小单元是行级数据

select name from user;
update  user set username = 'zkf' where sex = 'false';

即使是查询或者更新某个表中某个字段,对于RDBMS来说都是加载整行数据来进行数据的修改

因此RDBMS在处理单个数据的处理方式上,性能并不高,因为会做一些无用的IO操作。

1.3 HBase

官网地址: http://hbase.apache.org/

HBase是一种构建在HDFS之上的分布式、面向列的存储系统。在需要实时读写、随机访问超大规模数据集时,可 以使用HBase。

1.3.1 特点

  • 大:一个表可以有上亿行,上百万列

  • 面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。 结构稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。

  • 无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可 以有截然不同的列。

  • 数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时 的时间戳。

  • 数据类型单一:HBase中的数据都是字符串,没有类型。

1.3.2 HBase和关系数据库区别

  • 数据库类型:HBase中的数据类型都是字符串类型(string)

  • 数据操作:HBase只有普通的增删改查等操作,没有表之间的关联查询

  • 存储模式:HBase是基于列式存储模式,而RDBMS是基于行式存储的

  • 应用场景:HBase适合存储大量数据,查询效率极高

1.4 HBase 数据结构

Hbase 数据结构其实很简单,涉及4个名词:RowKey|Column Family |Cell|TimeStamps,在说数据结构之前先看一张真正的HBase的表,有助于理解HBase的数据结构

行健 时间戳 列 蔟 单元格(值)
“database.software.www” t1 anchor:name John
“database.software.www” t2 info:address BeiJing
“database.software.www” t3 anchor:name James
“database.software.www” t4 anchor:tel 01012345678
“database.software.www” t4 info:PC 100000
“c.software.www” t1 anchor:name James
“c.software.www” t2 anchor:tel 01012345678
“c.software.www” t3 info:address BeiJing

观察上述表格 ,这就是HBASE中存储数据的样子,可以看到HBASE行键是域名倒置的命名方式,时间戳就是当前时间,有不用的时间戳,在冒号之前就是列蔟的名字,但是你会发现,并不是每个的人列蔟都是相同的,你会发现每个人的列蔟都有可能不同,所以这就是体现出了HBase在存储数据的时候可以做到不存储某个属性的数据(在关系型数据中创建表之后,即使该属性没有数据也需要显示为null),这就是Hbase表存储稀疏的特点。

1.4.1 Rowkey | 行键

其实行键就是用来检索记录的主键

与 NoSQL 数据库一样,Row Key 是用来检索记录的主键 。访问HBase中的行,只有三种方式:
1. 通过单个RowKey访问
2. 通过Rowkey的range(正则)
3. 全表扫描Rowkey可以是任意字符串(注意:最大长度为64 KB),在HBase内部,Rowkey保存为字节数组。存储时,数据按照Rowkey的字典顺序(byte order)排序存储。设计Rowkey时,要充分考虑排序存储这个特性,将经常一起读取的行(IO特性相似的行)存储放到一起。(位置相关性)

1.4.2 Column Family | 列蔟

列蔟:HBase表中的每个列,都归属于某个列蔟,其实就是列的集合。列蔟是表的 Schema 的一部分(而列不是),必须在使用表之前定义。 列名都以列蔟作为前缀。

1.4.3 Cell

关键字:无类型、字节码

由rowkey、column Family :column (值),version 组成的唯一的单元,值得注意是这里的Cell并不单单指当前列蔟的值,而是指好几个属性加起来所表示唯一的值。Cell 中的数据是没有类型 的,全部是字节码形式存储。

HBase和关系数据库区别

  • 数据库类型:HBase中的数据类型都是字符串类型(string)

  • 数据操作:HBase只有普通的增删改查等操作,没有表之间的关联查询

  • 存储模式:HBase是基于列式存储模式,而RDBMS是基于行式存储的

  • 应用场景:HBase适合存储大量数据,查询效率极高

1.4.4 TimeStamps

HBase中通过rowkey和columns确定的为一个存储单元成为Cell。每个cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是64位整形。时间戳可以由HBase(在数据写入时 自动)赋值, 此时时间戳是精确到毫秒的当前系统时间。时间戳也 可以由客户显示赋值。如果应用程序要避 免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 Cell 中,不同版本的数据按照时间倒序排序, 即最新的数据排在最前面。

二、HBase 环境搭建-单机

2.1 基础环境

  • Hadoop

  • Zookeeper

2.2 安装和配置

[root@HadoopNode00 ~]# tar -zxvf hbase-1.2.4-bin.tar.gz -C /home/hbase/ # 解压至对应的目录
[root@HadoopNode00 ~]# vi .bashrc   # 配置habse 环境变量export HBASE_HOME=/home/hbase/hbase-1.2.4
export HBASE_MANAGES_ZK=false  # 使用外部ZK
export PATH=$PATH:$HBASE_HOME/bin[root@HadoopNode00 ~]# source .bashrc    # 使环境变量生效[root@HadoopNode00 ~]# vi /home/hbase/hbase-1.2.4/conf/hbase-site.xml<configuration><property><name>hbase.rootdir</name><value>hdfs://HadoopNode00:9000/hbase</value></property><property><name>hbase.cluster.distributed</name><value>true</value></property><property><name>hbase.zookeeper.quorum</name><value>HadoopNode00</value></property><property><name>hbase.zookeeper.property.clientPort</name><value>2181</value></property>
</configuration>[root@HadoopNode00 ~]# vi /home/hbase/hbase-1.2.4/conf/regionservers
HadoopNode00

2.3 启动

[root@HadoopNode00 ~]# start-dfs.sh   # 首先保证hdfs启动
[root@HadoopNode00 ~]# /home/zk/zookeeper-3.4.6/bin/zkServer.sh start /home/zk/zookeeper-3.4.6/conf/zk.cfg  # 首先保证zk启动
[root@HadoopNode00 ~]# start-hbase.sh  # 直接通过指令启动
[root@HadoopNode00 ~]# jps
1699 NameNode
2052 SecondaryNameNode
40660 QuorumPeerMain
42020 Jps
1851 DataNode
41708 HRegionServer  #  健康存活
18476 NodeManager
41548 HMaster        #  健康存活
18239 ResourceManager

2.4 连接

[root@HadoopNode00 ~]# hbase shell

2.5 Web UI

http://hostname:16010

三、Shell 操作

3.1 常见命令

status, table_help, version, whoami

hbase(main):002:0> status
1 active master, 0 backup masters, 1 servers, 0 dead, 2.0000 average load
hbase(main):003:0> version
1.2.4, rUnknown, Wed Feb 15 18:58:00 CST 2017hbase(main):004:0> whoami
root (auth:SIMPLE)groups: root

3.2 命名空间操作

alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables

创建命名空间

hbase(main):005:0> create_namespace 'zpark',{'user'=>'zhangsan'}
0 row(s) in 0.0560 seconds
#  需要注意的指定值为XXX 不能用等于好  而需要用‘=>’
hbase(main):006:0>  list_namespace
list_namespace          list_namespace_tables
hbase(main):006:0>  list_namespace
NAMESPACE
default
hbase
zpark
3 row(s) in 0.0470 seconds

描述命名空间

hbase(main):008:0> describe_namespace 'zpark'
DESCRIPTION
{NAME => 'zpark', user => 'zhangsan'}
1 row(s) in 0.0450 seconds

修改命名空间

hbase(main):009:0> alter_namespace 'zpark',{METHOD => 'set' ,'user'=>'guojiafeng'}
0 row(s) in 0.0460 secondshbase(main):010:0> describe_namespace 'zpark'
DESCRIPTION
{NAME => 'zpark', user => 'guojiafeng'}
1 row(s) in 0.0020 seconds

删除命名空间 属性

hbase(main):012:0> alter_namespace 'zpark',{METHOD => 'unset' ,NAME=>'user'}
0 row(s) in 0.0450 secondshbase(main):013:0> describe_namespace 'zpark'
DESCRIPTION
{NAME => 'zpark'}
1 row(s) in 0.0090 seconds

显示所有命名空间

hbase(main):015:0> list_namespace
NAMESPACE
default
hbase
zpark
3 row(s) in 0.0270 seconds

删除命名空间

hbase(main):016:0> drop_namespaceERROR: wrong number of arguments (0 for 1)Here is some help for this command:
Drop the named namespace. The namespace must be empty. # 命名空间必须为空hbase(main):017:0> drop_namespace 'zpark'
0 row(s) in 0.0480 seconds

显示某个命名空间下的表

hbase(main):003:0> list_namespace_tables 'baizhi'
TABLE
t_user
1 row(s) in 0.0330 seconds

3.3 DDL 数据定义语言

对命名空间(数据库)的中表进行操作

创建表

hbase(main):004:0> create 'baizhi:t_user','cf1','cf2'hbase(main):003:0> list_namespace_tables 'baizhi'
TABLE
t_user
1 row(s) in 0.0330 seconds

查看表详情

hbase(main):004:0> describe 'baizhi:t_user'
Table baizhi:t_user is ENABLED
baizhi:t_user
COLUMN FAMILIES DESCRIPTION
{NAME => 'cf1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODIN
G => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICA
TION_SCOPE => '0'}
{NAME => 'cf2', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODIN
G => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICA
TION_SCOPE => '0'}
2 row(s) in 0.1400 seconds

删除表

注意在删除前 需要先将 表 disable 掉

hbase(main):001:0> drop 'baizhi:t_user'ERROR: Table baizhi:t_user is enabled. Disable it first.Here is some help for this command:
Drop the named table. Table must first be disabled:hbase> drop 't1'hbase> drop 'ns1:t1'hbase(main):002:0> disable 'baizhi:t_user'
0 row(s) in 2.5940 secondshbase(main):003:0> drop 'baizhi:t_user'
0 row(s) in 1.5920 seconds

显示所有表

hbase(main):004:0> list
TABLE
0 row(s) in 0.0190 seconds

3.4 数据的CURD DML(数据管理语言)

插入(put)

# 插入一条数据 在baizhi:t_user  行健为1  列簇cf1 字段名为name 值为zs
hbase(main):012:0> put 'baizhi:t_user',1,'cf1:name','zs'
0 row(s) in 0.1420 seconds
hbase(main):001:0> t = get_table 'baizhi:t_user'   # 做表的引用
0 row(s) in 0.0330 seconds=> Hbase::Table - baizhi:t_user
# 插入一条数据 在baizhi:t_user  行健为1  列簇cf1 字段名为sex 值为true
hbase(main):002:0> t.put 1,'cf1:sex','true'
0 row(s) in 0.3510 seconds
# 插入一条数据 在baizhi:t_user  行健为1  列簇cf1 字段名为age 值为18
hbase(main):003:0> t.put 1,'cf1:age',18
0 row(s) in 0.0210 secondshbase(main):004:0> t.get 1
COLUMN                           CELLcf1:age                         timestamp=1572888118460, value=18cf1:name                        timestamp=1572887983788, value=zscf1:sex                         timestamp=1572888095574, value=true
3 row(s) in 0.0500 seconds

更新

# 创建一个可以多个版本的表
hbase(main):005:0> create 'baizhi:t_user',{NAME=>'cf1',VERSIONS=>3},{NAME=>'cf2',VERSIONS=>3}
0 row(s) in 1.2410 seconds=> Hbase::Table - baizhi:t_user
# 插入基础的数据
hbase(main):012:0> t.put 1,'cf1:name','zs'
0 row(s) in 0.0870 seconds
hbase(main):013:0> t.put 1,'cf1:age',18
0 row(s) in 0.0220 seconds
hbase(main):014:0> t.put 1,'cf1:sex',true
0 row(s) in 0.0270 seconds# 进行数据的更新
hbase(main):015:0> t.put 1,'cf1:name','zhangsan'
0 row(s) in 0.0080 seconds# 可以看到数据已经变为其他数据
hbase(main):016:0> t.get 1
COLUMN                           CELLcf1:age                         timestamp=1572888599018, value=18cf1:name                        timestamp=1572888630027, value=zhangsancf1:sex                         timestamp=1572888605427, value=true
3 row(s) in 0.0350 seconds

取值(get)

# 获取所有rowkey 为1 列簇为cf1  列名为name  最多获取三个版本的数据
hbase(main):017:0> t.get 1 ,{COLUMNS=>'cf1:name',VERSIONS=>3}
COLUMN                           CELLcf1:name                        timestamp=1572888630027, value=zhangsancf1:name                        timestamp=1572888590148, value=zs
2 row(s) in 0.0300 seconds# 根据某个时间戳进行获取
hbase(main):020:0> t.get 1 ,{COLUMNS=>'cf1:name',TIMESTAMP => 1572888590148}
COLUMN                           CELLcf1:name                        timestamp=1572888590148, value=zs
1 row(s) in 0.0170 seconds# 根据时间戳区间进行获取hbase(main):024:0> t.get 1 ,{COLUMNS=>'cf1:name',TIMERANGE => [157288850147,1572888630030],VERSIONS => 4}
COLUMN                           CELLcf1:name                        timestamp=1572888630027, value=zhangsancf1:name                        timestamp=1572888590148, value=zs
2 row(s) in 0.0160 seconds

删除(delete/deleteall)

# 直接进行删除
hbase(main):025:0> delete 'baizhi:t_user',1,'cf1:name'
0 row(s) in 0.0560 seconds
# 引用删除
hbase(main):027:0> t.delete 1,'cf1:sex'
0 row(s) in 0.0290 seconds
# 删除某个id下的所有数据
hbase(main):029:0> t.deleteall 1
0 row(s) in 0.0150 seconds# 删除某个id某个列簇某个字段所有版本的值
hbase(main):043:0> t.deleteall 1 ,'cf1:name'
0 row(s) in 0.0150 seconds

全表扫描

hbase(main):049:0> t.scan
ROW                              COLUMN+CELL1                               column=cf1:age, timestamp=1572889604496, value=181                               column=cf1:name, timestamp=1572889601629, value=zs1                               column=cf1:sex, timestamp=1572889608032, value=true
1 row(s) in 0.0240 seconds

计数(count)

hbase(main):050:0> t.count
1 row(s) in 0.0590 seconds
=> 1

追加(append)

hbase(main):051:0> t.append 1,'cf1:name','123'
0 row(s) in 0.0220 seconds
hbase(main):052:0> t.scan
ROW                              COLUMN+CELL1                               column=cf1:age, timestamp=1572889604496, value=181                               column=cf1:name, timestamp=1572889732899, value=zs1231                               column=cf1:sex, timestamp=1572889608032, value=true
1 row(s) in 0.0240 seconds

清空数据

hbase(main):053:0> truncate 'baizhi:t_user'
Truncating 'baizhi:t_user' table (it may take a while):- Disabling table...- Truncating table...
0 row(s) in 3.9640 secondshbase(main):054:0> t.scan
ROW                              COLUMN+CELL
0 row(s) in 0.1440 seconds

四、Java API

4.1 依赖

 <dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-client</artifactId><version>1.2.4</version>
</dependency>

4.2 获取客户端

private Connection connection;private Admin admin;@Beforepublic void getAdmin() throws Exception {Configuration conf = new Configuration();conf.set("hbase.zookeeper.quorum", "HadoopNode00");conf.set("hbase.zookeeper.property.clientPort", "2181");connection = ConnectionFactory.createConnection(conf);admin = connection.getAdmin();}

4.3 关闭资源

@Afterpublic void close() throws Exception {admin.close();connection.close();}

4.4 命名空间操作

创建命名空间

 @Testpublic void createNameSpace() throws Exception {NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create("hadoop1").addConfiguration("baizhi", "123").build();admin.createNamespace(namespaceDescriptor);}

修改命名空间

 @Testpublic void changeNameSpace()  throws Exception{NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create("hadoop").removeConfiguration("baizhi").build();admin.modifyNamespace(namespaceDescriptor);}

删除命名空间

@Testpublic void deleteNameSpace() throws Exception{admin.deleteNamespace("hadoop");}

列出命名空间

@Testpublic void listNameSpace() throws Exception {NamespaceDescriptor[] namespaceDescriptors = admin.listNamespaceDescriptors();for (NamespaceDescriptor namespaceDescriptor : namespaceDescriptors) {System.out.println(namespaceDescriptor.getName());}}

4.5 表操作

创建表

 @Testpublic void createTable() throws Exception {/** 将表的名字信息封装到TableName中* */TableName tableName = TableName.valueOf("baizhi:t_java");/*** 创建描述表的对象 并提供表的名字* */HTableDescriptor tableDescriptor = new HTableDescriptor(tableName);/** 描述列簇的对象  并指定列簇的名字* */HColumnDescriptor cf1 = new HColumnDescriptor("cf1");// 设置 最大可存的版本cf1.setMaxVersions(3);/** 描述列簇的对象  并指定列簇的名字* */HColumnDescriptor cf2 = new HColumnDescriptor("cf2");// 设置 最大可存的版本cf2.setMaxVersions(3);// 在表中添加必要的属性:列簇tableDescriptor.addFamily(cf1);tableDescriptor.addFamily(cf2);/** 使用admin对象创建表* */admin.createTable(tableDescriptor);}

删除表

    @Testpublic void deleteTable() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_java");if (admin.tableExists(tableName)) {admin.disableTable(tableName);admin.deleteTable(tableName);}}

4.6 CURD

put

插入|更新单个记录

@Testpublic void putData() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_user");Table table = connection.getTable(tableName);/** 封装  一行 数据* */Put put = new Put("1".getBytes());/** 参数列表  :  列簇  列名  值* */put.addColumn("cf1".getBytes(), "name".getBytes(), "zhangsan".getBytes());put.addColumn("cf1".getBytes(), "pwd".getBytes(), "123".getBytes());put.addColumn("cf2".getBytes(), "age".getBytes(), "18".getBytes());put.addColumn("cf2".getBytes(), "salary".getBytes(), "1000".getBytes());table.put(put);table.close();}

批量插入

 @Testpublic void putManyData() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_user");Table table = connection.getTable(tableName);/** 封装  一行 数据* */Put put = new Put("2".getBytes());/** 参数列表  :  列簇  列名  值* */put.addColumn("cf1".getBytes(), "name".getBytes(), "lisi".getBytes());put.addColumn("cf1".getBytes(), "pwd".getBytes(), "123".getBytes());put.addColumn("cf2".getBytes(), "age".getBytes(), "20".getBytes());put.addColumn("cf2".getBytes(), "salary".getBytes(), "20000".getBytes());ArrayList<Put> puts = new ArrayList<Put>();puts.add(put);table.put(puts);table.close();}
@Testpublic void putManyData() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_user");BufferedMutator bufferedMutator = connection.getBufferedMutator(tableName);/** 封装  一行 数据* */Put put = new Put("2".getBytes());/** 参数列表  :  列簇  列名  值* */put.addColumn("cf1".getBytes(), "name".getBytes(), "ls".getBytes());put.addColumn("cf1".getBytes(), "pwd".getBytes(), "123".getBytes());put.addColumn("cf2".getBytes(), "age".getBytes(), "20".getBytes());put.addColumn("cf2".getBytes(), "salary".getBytes(), "20000".getBytes());ArrayList<Put> puts = new ArrayList<Put>();puts.add(put);bufferedMutator.mutate(puts);bufferedMutator.close();}

delete

@Testpublic void deleteData() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_user");Table table = connection.getTable(tableName);Delete delete = new Delete("2".getBytes());table.delete(delete);table.close();}

批量删除

@Testpublic void deleteManyData() throws Exception {TableName tableName = TableName.valueOf("baizhi:t_user");Table table = connection.getTable(tableName);Delete delete = new Delete("1".getBytes());table.delete(delete);table.close();}

get

 @Testpublic void getData() throws Exception{Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));Get get = new Get("2".getBytes());Result result = table.get(get);/** 列簇    列名* */byte[] name = result.getValue("cf1".getBytes(), "name".getBytes());byte[] pwd = result.getValue("cf1".getBytes(), "pwd".getBytes());byte[] age = result.getValue("cf2".getBytes(), "age".getBytes());byte[] salary = result.getValue("cf2".getBytes(), "salary".getBytes());System.out.println("名字为:"+Bytes.toString(name)+", 密码为:"+Bytes.toString(pwd)+",年龄为:"+Bytes.toString(age)+",工资为:"+Bytes.toString(salary));}

获取多个版本的数据

 @Testpublic void getManyData() throws Exception {Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));Get get = new Get("2".getBytes());get.setMaxVersions(3);get.addColumn("cf1".getBytes(), "name".getBytes());Result result = table.get(get);List<Cell> columnCells = result.getColumnCells("cf1".getBytes(), "name".getBytes());for (Cell columnCell : columnCells) {byte[] rowData = CellUtil.cloneRow(columnCell);byte[] cfData = CellUtil.cloneFamily(columnCell);byte[] qualifierData = CellUtil.cloneQualifier(columnCell);byte[] data = CellUtil.cloneValue(columnCell);System.out.println("行健为:" + Bytes.toString(rowData) + ", 列簇为:" + Bytes.toString(cfData) + ",列名为:" + Bytes.toString(qualifierData) + ",名字为:" + Bytes.toString(data));}}

scan

@Testpublic void scanData() throws Exception {Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));Scan scan = new Scan();// scan.addFamily("cf1".getBytes());//scan.addColumn("cf1".getBytes(),"name".getBytes());PrefixFilter prefixFilter1 = new PrefixFilter("1".getBytes());PrefixFilter prefixFilter2 = new PrefixFilter("2".getBytes());FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE, prefixFilter1, prefixFilter2);scan.setFilter(filterList);ResultScanner scanner = table.getScanner(scan);for (Result result : scanner) {System.out.println("------------------");byte[] name = result.getValue("cf1".getBytes(), "name".getBytes());byte[] pwd = result.getValue("cf1".getBytes(), "pwd".getBytes());byte[] age = result.getValue("cf2".getBytes(), "age".getBytes());byte[] salary = result.getValue("cf2".getBytes(), "salary".getBytes());System.out.println("名字为:" + Bytes.toString(name) + ", 密码为:" + Bytes.toString(pwd) + ",年龄为:" + Bytes.toString(age) + ",工资为:" + Bytes.toString(salary));}scanner.close();table.close();}

五、MR On HBase

5.1 依赖

      <dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-server</artifactId><version>1.2.4</version></dependency><dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-client</artifactId><version>1.2.4</version></dependency><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-mapreduce-client-core</artifactId><version>2.6.0</version></dependency><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-mapreduce-client-jobclient</artifactId><version>2.6.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common --><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-common</artifactId><version>2.6.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs --><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-hdfs</artifactId><version>2.6.0</version></dependency>
 @Testpublic void putData02() throws Exception {/**ali baidu sina** rowkey :公司名称+编号* */BufferedMutator mutator = connection.getBufferedMutator(TableName.valueOf("baizhi:t_user1"));String[] company = {"baidu", "ali", "sina"};ArrayList<Put> puts = new ArrayList<Put>();for (int i = 0; i < 1000; i++) {String companyName = company[new Random().nextInt(3)];String empid = "";if (i < 10) {empid = "00" + i;} else if (i < 100) {empid = "0" + i;} else {empid = "" + i;}String rowKey = companyName + ":" + empid;Put put = new Put(rowKey.getBytes());put.addColumn("cf1".getBytes(), "name".getBytes(), ("zs" + i).getBytes());put.addColumn("cf1".getBytes(), "salary".getBytes(), Bytes.toBytes(100.0 * i));put.addColumn("cf2".getBytes(), "age".getBytes(), "18".getBytes());put.addColumn("cf2".getBytes(), "sex".getBytes(), "true".getBytes());puts.add(put);}mutator.mutate(puts);mutator.close();}
package com.baizhi.mrdemo;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.mapreduce.TableInputFormat;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.MRJobConfig;public class AVGJob {public static void main(String[] args) throws Exception {System.setProperty("HADOOP_USER_NAME", "root");Configuration conf = new Configuration();/** 设置跨平台提交* */conf.set("mapreduce.app-submission.cross-platform", "true");conf.addResource("conf2/core-site.xml");conf.addResource("conf2/hdfs-site.xml");conf.addResource("conf2/mapred-site.xml");conf.addResource("conf2/yarn-site.xml");conf.set("hbase.zookeeper.quorum", "HadoopNode00");conf.set("hbase.zookeeper.property.clientPort", "2181");conf.set(MRJobConfig.JAR, "D:\\大数据训练营课程\\Code\\BigData\\HBase_Test\\target\\HBase_Test-1.0-SNAPSHOT.jar");Job job = Job.getInstance(conf);job.setInputFormatClass(TableInputFormat.class);job.setOutputFormatClass(TableOutputFormat.class);TableMapReduceUtil.initTableMapperJob("baizhi:t_user1",new Scan(),AVGMapper.class,Text.class,DoubleWritable.class,job);TableMapReduceUtil.initTableReducerJob("baizhi:t_result",AVGReducer.class,job);job.waitForCompletion(true);}
}
package com.baizhi.mrdemo;import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;import java.io.IOException;public class AVGMapper extends TableMapper<Text, DoubleWritable> {@Overrideprotected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {/** key  为rowky* value 为rowky 下所代表的值* *//** 获取到rowkey的值* */byte[] bytes = key.get();/** 转成字符串进行分割  获取下标为0的公司名字* */String companyName = Bytes.toString(bytes).split(":")[0];byte[] salaryBytes = value.getValue("cf1".getBytes(), "salary".getBytes());/** 工资数据* */double salary = Bytes.toDouble(salaryBytes);context.write(new Text(companyName), new DoubleWritable(salary));}
}
package com.baizhi.mrdemo;import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;import java.io.IOException;public class AVGReducer extends TableReducer<Text, DoubleWritable, NullWritable> {@Overrideprotected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException {double totalSalary = 0;int countPeople = 0;for (DoubleWritable value : values) {totalSalary += value.get();countPeople++;}Put put = new Put(key.getBytes());put.addColumn("cf1".getBytes(), "avgSalary".getBytes(), (totalSalary / countPeople + "").getBytes());context.write(NullWritable.get(), put);}
}

六 、HBase架构

6.1 Zookeeper

HBase通过zk来做Master的高可用,RegionServer的监控、元数据的入口以及集群配置的维护工作;具体如下:

通过ZK保证集群中只有一个Master可用,在主的Master的出现异常后,会通过竞争机制产生新的Master

通过ZK监控RegionServer的状态,当RegionServer有异常时,同时Master RegionServer上下线信息。

通过ZK存储元数据

ZK作为统一的入口地址

6.2 HMaster

为RegionServer分配Region,维护集群的负载均衡,维护集群的元数据信息,发现失效的Region,将失效的Region分配到正常的RegionServer上,当RegionServer失效时,协调对应的HLog的拆分。

6.3 HRegionServer

HRegionServer直接对接用户的读写请求,是真正的干活的额节点,功能如下:

管理Master为其分配的Region

处理来自客户端的读写请求,负责与HDFS进行交互,负责Region变大后的拆分。

6.3.1 Region

  • HBase 表根据Rowkey 划分成Region,理论上一个Region包含该表格从起始行到结束之间的所有行。

  • 但是往往有可能一个Region没有办法存储这个表中所有的行,会进行切分 数据大小>=N^2*128MB(N为Region数量),当N=9时,切分大小会超过10GB,此时就按照10GB进行切分。

  • Region由Store 组成

  • Region会被分配到称之为“HRegionServer”的节点上

Store

每一个Region由一个或者多个Store组成,至少是一个Store 。HBase会把经常访问的数据放在一个Store里面,即一个列簇组成一个Store ,有多少个列簇就多少个Store 。

在Store 中,由一个memStore 和 0个或者多个StoreFile 组成

MemStore
  • 写缓存。数据先写入到MemStore,触发flush机制后写到磁盘中
  • 以key-value的形式存储到内存中
  • MemStore的6种Flush机制(当MemStore Flush触发后,同一Region下的所有的MemStore都会刷新)
    • MemStore级别:当Region中人一个MemStore的大小达到上限(默认为128MB hbase.hregion.memstore.flush.size),会触发MemStore刷新
    • Region级别:当Region中所有的MemStore大小总和达到的上限(256MB hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M)
    • RegionServer级别:当RegionServer中所有的MemStore大小总和达到上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
    • 当一个RegionServer中HLog数量达到了上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
    • HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
    • 手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。
StoreFile|HFile

HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、 Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。

由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,由于不需要不停的移动磁盘指针,因此磁盘的顺序写性能很高。

HFile的组成分为6部分,分别是数据块、元数据块、FileInfo块、数据索引块、元数据索引块、HFile文件尾,它们的详细描述如下:

名称 描述
数据块 由多个Block(块)组成,每个块的格式为: [块头] + [Key长] + [Value长] + [Key] + [Value]。
元数据块 元数据是Key-Value类型的值,但元数据块只保存元数据的Value值,元数据的Key值保存在第五项(元数据索引块)中。 该块由多个元数据值组成。
FileInfo块 该块保存与HFile相关的一些信息。 FileInfo是以Key值排序Key-Value类型的值,基本格式为: KeyValue元素的个数 + (Key + Value类型id + Value) + (Key + Value类型id + Value) + ……
数据索引块 该块的组成为: 索引块头 + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + ……
元数据索引块 该块组成格式同数据块索引,只是部分的意义不一样,组成格式: 索引块头 + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + ……
HFile文件尾 该块记录了其他各块在HFile文件中的偏移信息和其他一些元信息。组成格式如下: 文件尾 + Fileinfo偏移 + 数据块索引偏移 + 数据块索引个数 + 元数据索引偏移 + 元数据索引个数 + 数据块中未压缩数据字节数 + 数据块中全部数据的Key-Value个数 + 压缩代码标识 + 版本标识

6.3.2 WAL

Write Ahead Log | HLog

  • 一个文件

  • 0.94之前叫做HLog,存储在/hbase/.logs/目录中

  • 0.94之后存储在HDFS上的**/hbase/WALs/{HRegionServer_name}**中

  • 记录RegionServer 上所有的编辑信息(Puts/Deletes,属于哪个Region),在写入MemStore之前。

  • 理论上一个RegionServer上只有一个WAL实例,数据操作为串行,造成性能瓶颈。在1.0之后 ,可以通过使用底层HDFS的多管道实现了多WAL并行写入。提高了吞吐量(但是并行化是通过对多个Region进行分区实现的,如果只有一个Region那么该方案无效)下面是配置多WAL示例:hbase-site.xml 配置

<property>   <name>hbase.wal.provider</name>   <value>multiwal</value>
</property>

6.4 BlockCache|HBase Block(了解)

  • 读缓存,数据被读取之后仍然缓存在内存中
  • 有LruBlockCache(效率较高,GC压力大)和BucketCache(效率较低,没有GC压力)两种BlockCache,默认为LruBlockCache
  • 每个RegionServer中只有一个BlockCache实例

6.5 HDFS

HDFS为HBase提供最终的底层数据存储服务,同时为HBase提供高可用(HLog存储在HDFS)的支持,具体功能概括如下:

提供元数据和表数据的磁层分布式存储服务

数据多副本,保证高可靠性和高可用性

七、HBase 读写流程(了解)

读流程

HBASE读数据流程
1 客户端先访问ZK 从meta表读取Region的位置
2 根据相关信息获取到Regiog所在的位置
3 找到Region对应的RegionServer
4 查找对应的Region
5 在对应的节点中区获取数据(如果MemStore没有刷新,则先从内存中获取数据,如果没有则向HFile中获取数据)

写流程

(1)Client 向HRegionServer发送请求
(2)HRegionServer写入数据到HLog中
(3)HRegionServer写入数据MemStore中
(4)向Client反馈写成功
HBase写数据流程
1 Client首先访问zookeeper,从meta表获取相应region信息,然后找到meta表的数据
2 根据namespace、表名和rowkey根据meta表的数据找到写入数据对应的region信息
3 找到对应的regionserver
4 把数据分别写到HLog和MemStore上一份
4 MemStore达到一个阈值后则把数据刷成一个StoreFile文件。(若MemStore中的数据有丢失,则可以从HLog上恢复)
5 当多个StoreFile文件达到一定的大小后,会触发Compact合并操作,合并为一个StoreFile,(这里同时进行版本的合并和数据删除。)
6 当Storefile大小超过一定阈值后,会把当前的Region分割为两个(Split),并由Hmaster分配到相应的HRegionServer,实现负载均衡

八、HBase 集群

HadoopNode01 HadoopNode02 HadoopNode03
Zookeeper Zookeeper Zookeeper
NameNode(Active) NameNode( Standby)
ZKFC ZKFC
JournalNode JournalNode JournalNode
DataNode DataNode DataNode
ResourceManager ResourceManager
NodeManger NodeManger NodeManger
HMaster HMaster HMaster
HRegionServer HRegionServer HRegionServer
  • 必须安装Hadoop并且配置HADOOP_HOME
  • 必须安装Zookeeper,并且保证的ZK正常启动
  • 安装配置HBase
[root@HadoopNodeX ~]# mkdir /home/hbase
[root@HadoopNodeX ~]# tar -zxvf hbase-1.2.4-bin.tar.gz -C /home/hbase/
[root@HadoopNodeX ~]# vi .bashrc
export JAVA_HOME=/home/java/jdk1.8.0_181
export HADOOP_HOME=/home/hadoop/hadoop-2.6.0
export HBASE_HOME=/home/hbase/hbase-1.2.4
export HBASE_MANAGES_ZK=false
export MAVEN_HOME=/home/maven/apache-maven-3.3.9
export M2_HOME=/home/maven/apache-maven-3.3.9
export PROTOBUF_HOME=/home/protobuf/protobuf-2.5.0
export FINDBUGS_HOME=/home/findbugs/findbugs-3.0.1
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$M2_HOME/bin:$PROTOBUF_HOME/bin:$FINDBUGS_HOME/bin:$HADOOP_HOME/sbin:$HADOOP_HOME/bin:$HBASE_HOME/bin
[root@HadoopNodeX ~]# source .bashrc[root@HadoopNodeX ~]# vi /home/hbase/hbase-1.2.4/conf/hbase-site.xml<property><name>hbase.rootdir</name><value>hdfs://mycluster/hbase</value></property><property><name>hbase.cluster.distributed</name><value>true</value></property><property><name>hbase.zookeeper.quorum</name><value>HadoopNode01,HadoopNode02,HadoopNode03</value></property><property><name>hbase.zookeeper.property.clientPort</name><value>2181</value></property>[root@HadoopNode01 ~]# vi /home/hbase/hbase-1.2.4/conf/regionservers
HadoopNode01
HadoopNode02
HadoopNode03

启动HBase

先启动HDFS YRAN  zk
[root@HadoopNodeX ~]# hbase-daemon.sh start master

ir /home/hbase
[root@HadoopNodeX ~]# tar -zxvf hbase-1.2.4-bin.tar.gz -C /home/hbase/
[root@HadoopNodeX ~]# vi .bashrc
export JAVA_HOME=/home/java/jdk1.8.0_181
export HADOOP_HOME=/home/hadoop/hadoop-2.6.0
export HBASE_HOME=/home/hbase/hbase-1.2.4
export HBASE_MANAGES_ZK=false
export MAVEN_HOME=/home/maven/apache-maven-3.3.9
export M2_HOME=/home/maven/apache-maven-3.3.9
export PROTOBUF_HOME=/home/protobuf/protobuf-2.5.0
export FINDBUGS_HOME=/home/findbugs/findbugs-3.0.1
export PATH=PATH:PATH:PATH:JAVA_HOME/bin:MAVENHOME/bin:MAVEN_HOME/bin:MAVENH​OME/bin:M2_HOME/bin:PROTOBUFHOME/bin:PROTOBUF_HOME/bin:PROTOBUFH​OME/bin:FINDBUGS_HOME/bin:HADOOPHOME/sbin:HADOOP_HOME/sbin:HADOOPH​OME/sbin:HADOOP_HOME/bin:$HBASE_HOME/bin
[root@HadoopNodeX ~]# source .bashrc

[root@HadoopNodeX ~]# vi /home/hbase/hbase-1.2.4/conf/hbase-site.xml

hbase.rootdir hdfs://mycluster/hbase hbase.cluster.distributed true hbase.zookeeper.quorum HadoopNode01,HadoopNode02,HadoopNode03 hbase.zookeeper.property.clientPort 2181

[root@HadoopNode01 ~]# vi /home/hbase/hbase-1.2.4/conf/regionservers
HadoopNode01
HadoopNode02
HadoopNode03


## 启动HBase

先启动HDFS YRAN zk


~~~shell
[root@HadoopNodeX ~]# hbase-daemon.sh start master

Apache HBase_GJF_MBY相关推荐

  1. Docker安装Apache与运行简单的web服务——httpd helloworld

    Docker运行简单的web服务--httpd helloworld目录[阅读时间:约5分钟] 一.Docker简介 二.Docker的安装与配置[CentOS环境] 三.Docker运行简单的web ...

  2. Apache Maven 安装与配置-修改源

    Maven配置,强大的Java包管理器 Maven介绍 Apache Maven 下载 Apache Maven 安装 要求 启动 解压到适当的文件夹 添加环境变量 修改配置 启动测试 Maven介绍 ...

  3. debian10 简单的bash脚本监控apache运行状态

    需求: 在Rserver上编写脚本监控公司的网站运行情况: 脚本可以在后台持续运行: 每隔3S检查一次网站的运行状态,如果发现异常尝试3次: 如果确定网站无法访问,则返回用户"网站正在维护中 ...

  4. Apache POI:解决数据库和Excel之间相互转换的烦恼~

    目录 引言 一.简介 二.POI-Excel 写 1.创建项目 2.引入依赖 3.步骤 1. 创建工作簿 2. 创建工作表 3. 创建行 4. 创建单元格 5. 单元格中填入数据 6. 通过IO流生成 ...

  5. 使用Apache TVM将机器学习编译为WASM和WebGPU

    使用Apache TVM将机器学习编译为WASM和WebGPU TLDR 在Apache TVM深度学习编译器中引入了对WASM和WebGPU的支持.实验表明,在将模型部署到Web时,TVM的WebG ...

  6. 大规模数据处理Apache Spark开发

    大规模数据处理Apache Spark开发 Spark是用于大规模数据处理的统一分析引擎.它提供了Scala.Java.Python和R的高级api,以及一个支持用于数据分析的通用计算图的优化引擎.它 ...

  7. 2021年大数据ELK(二十二):采集Apache Web服务器日志

    全网最详细的大数据ELK文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 采集Apache Web服务器日志 一.需求 二.准备日志数据 三.使用Fil ...

  8. 2021年大数据HBase(十二):Apache Phoenix 二级索引

    全网最详细的大数据HBase文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 系列历史文章 前言 Apache Phoenix 二级索引 一.索引分类 ...

  9. 2021年大数据HBase(十一):Apache Phoenix的视图操作

    全网最详细的大数据HBase文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 系列历史文章 前言 Apache Phoenix的视图操作 一.应用场景 ...

最新文章

  1. python经典案例-Python递归的经典案例
  2. timestamp(6) oracle计算差值_Oracle 计算两个时间的差值
  3. 【控制】《多智能体系统的协同群集运动控制》陈杰老师-第2章-连通性保持条件下多智能体系统群集运动控制
  4. 如何成为一名合格的推荐系统工程师?
  5. 程序显示文本框_C++入门到精通(二),编写我们自己的Hello World程序
  6. 前端学习(2326):angular之用户输入数据
  7. 单行文本与多行文本省略文本
  8. android开发——手机通话功能实现
  9. 面试准备每日五题:C++(五)——野指针、句柄指针、newdeletemallocfree、extern、类成员权限
  10. 速更新!流行的开源邮件客户端 Mozilla Thunderbird 91.3修复多个高危缺陷
  11. c语言串逐位和 思路,蓝桥杯c语言练习题答案.doc
  12. 数据压缩算法该如何选择?
  13. 黑客泄露澳大利亚大量求职者信息,其中包含11家重要公司名单
  14. 2012年8月 发散的安全焦点
  15. CCS导入工程时报错“overlaps the location of another project”解决办法
  16. 数学笔记24——分部积分
  17. A Survey on Metaverse: the State-of-the-art,Technologies, Applications, and Challenges
  18. php读写w,PHP如何在读写(w +)模式下创建临时文件?,_PHP_ 少侠科技
  19. LVM-HOWTO/学习笔记(五)
  20. 【RISC-V】嵌入式开发小技巧之代码字体缩放!

热门文章

  1. 专升本——非谓语动词
  2. python socket 编程之三:长连接、短连接以及心跳(转药师Aric的文章)...
  3. 深度解析,单片机运行原理,你知道吗?
  4. html403禁止访问怎么解决,http出现“禁止访问 403”错误的起因和解决方法
  5. 中职 网络搭建与应用 DCN无线常用配置
  6. 【进程调度】模拟进程调度的过程
  7. Excel插入XactiveX提示出错“不能插入对象”解决
  8. 分销商城小程序开发解决方案
  9. 什么是面向对象?你是怎么理解面向对象的?为什么要用面向对象?用面向对象有什么好处?
  10. 微信小程序wxs将数字转换为汉字