文章目录
-
-
- HBase简介
- [HBase 入门](#HBase 入门)
-
- [**HBase** **安装部署**](#HBase 安装部署)
- [**HBase** 配置文件](#HBase 配置文件)
- [**HBase** 启动停止](#HBase 启动停止)
- [**HBase** **访问页面**](#HBase 访问页面)
- [**HBase** **高可用**](#HBase 高可用)
- [**HBase Shell**](#HBase Shell)
- [**HBase API**](#HBase API)
- [**HBase** **进阶**](#HBase 进阶)
- [HBase 优化](#HBase 优化)
- [集成 Phoenix](#集成 Phoenix)
- [集成 Hive](#集成 Hive)
-
HBase简介
HBase定义
Apache HBase 是以 hdfs 为数据存储的,一种分布式、可扩展的 NoSQL 数据库
HBase数据模型
HBase 的设计理念依据 Google 的 BigTable 论文,论文中对于数据模型的首句介绍。
Bigtable 是一个稀疏的、分布式的、持久的多维排序 map。之后对于映射的解释如下:
该映射由行键、列键和时间戳索引;映射中的每个值都是一个未解释的字节数组。
最终 HBase 关于数据模型和 BigTable 的对应关系如下:
HBase 使用与 Bigtable 非常相似的数据模型。用户将数据行存储在带标签的表中。
数据行具有可排序的键和任意数量的列。
该表存储稀疏,因此如果用户喜欢,同一表中的行可以具有疯狂变化的列。
最终理解 HBase 数据模型的关键在于稀疏、分布式、多维、排序的映射。
其中映射 map指代非关系型数据库的 key-Value 结构。
1)Name Space
命名空间,类似于关系型数据库的 database 概念,每个命名空间下有多个表。HBase 两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default表是用户默认使用的命名空间。
2)Table
类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。因为数据存储时稀疏的,所有往 HBase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。
3)Row
HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要。
4)Column
HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。
5)Time Stamp
用于标识数据的不同版本(version),每条数据写入时,系统会自动为其加上该字段,其值为写入 HBase 的时间。
6)Cell
由{rowkey, column Family:column Qualifier, timestamp} 唯一确定的单元。
cell 中的数据全部是字节码形式存贮。
HBase 逻辑结构
存储数据稀疏,数据存储多维,不同的行具有不同的列。
数据存储整体有序,按照RowKey的字典序排列,RowKey为Byte数组

HBase 物理存储结构
物理存储结构即为数据映射关系,而在概念视图的空单元格,底层实际根本不存储。

HBase 基本架构
Master
主要进程,具体实现类为HMaster,通常部署在namenode上。
功能:负责通过ZK监控RegionServer进程状态,同时是所有元数据变化的接口。内部启动监控执行region的故障转移和拆分的线程。
RegionServer
主要进程,具体实现类为HRegionServer,部署在datanode上。
功能:主要负责数据cell的处理。同时在执行区域的拆分和合并的时候,由RegionServer来实际执行。

架构角色:
1)Master
实现类为 HMaster,负责监控集群中所有的 RegionServer 实例。主要作用如下:
(1)管理元数据表格 hbase:meta,接收用户对表格创建修改删除的命令并执行
(2)监控 region 是否需要进行负载均衡,故障转移和 region 的拆分。
通过启动多个后台线程监控实现上述功能:
①LoadBalancer 负载均衡器
周期性监控 region 分布在 regionServer 上面是否均衡,
由参数 hbase.balancer.period 控制周期时间,默认 5 分钟。
②CatalogJanitor 元数据管理器
定期检查和清理 hbase:meta 中的数据。meta 表内容在进阶中介绍。
③MasterProcWAL master 预写日志处理器
把 master 需要执行的任务记录到预写日志 WAL 中,如果 master 宕机,让 backupMaster读取日志继续干。
2)Region Server
Region Server 实现类为 HRegionServer,主要作用如下:
(1)负责数据 cell 的处理,例如写入数据 put,查询数据 get 等
(2)拆分合并 region 的实际执行者,有 master 监控,有 regionServer 执行。
3)Zookeeper
HBase 通过 Zookeeper 来做 master 的高可用、记录 RegionServer 的部署信息、并且存储有 meta 表的位置信息。
HBase 对于数据的读写操作时直接访问 Zookeeper 的,在 2.3 版本推出 Master Registry模式,客户端可以直接访问 master。使用此功能,会加大对 master 的压力,减轻对 Zookeeper的压力。
4)HDFS
HDFS 为 Hbase 提供最终的底层数据存储服务,同时为 HBase 提供高容错的支持。
HBase 入门
HBase 安装部署
略
HBase 配置文件
略
HBase 启动停止
略
HBase 访问页面
启动成功后,可以通过"host:port"的方式来访问 HBase 管理页面,例如:http://hadoop:16010

HBase 高可用
在 HBase 中 HMaster 负责监控 HRegionServer 的生命周期,均衡 RegionServer 的负载,
如果 HMaster 挂掉了,那么整个 HBase 集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。
所以 HBase 支持对 HMaster 的高可用配置。
1)关闭 HBase 集群(如果没有开启则跳过此步)
atguigu@hadoop102 hbase\]$ bin/stop-hbase.sh
2)在 **conf** **目录下创建** **backup-masters** **文件**
\[atguigu@hadoop102 hbase\]$ touch conf/backup-masters
3)在**backup-masters** **文件中配置高可用** **HMaster** **节点**
\[atguigu@hadoop102 hbase\]$ echo hadoop103 \> conf/backup-masters
4)将整个 **conf** **目录** **scp** **到其他节点**
\[atguigu@hadoop102 hbase\]$ xsync conf
5)重启 hbase,打开页面测试查看
##### **HBase Shell**
```shell
#进入 HBase 客户端命令行
bin/hbase shell
#特定的 help 语法能够查看命令如何使用
help 'create_namespace'
#创建命名空间 bigdata
create_namespace 'bigdata'
#查看所有的命名空间
list_namespace
======================DDL======================
#在 bigdata 命名空间中创建表格 student,两个列族。
#info 列族数据维护的版本数为 5 个,如果不写默认版本数为 1。
create 'bigdata:student', {NAME => 'info', VERSIONS => 5}, {NAME => 'msg'}
#如果创建表格只有一个列族,没有列族属性,可以简写。如果不写命名空间,使用默认的命名空间 default。
create 'student1','info'
#查看表有两个命令:list 和 describe
#查看所有的表名
list
#查看一个表的详情
describe 'student1'
===========修改表===========
#表名创建时写的所有和列族相关的信息,都可以后续通过 alter 修改,包括增加删除列族
#增加列族和修改信息都使用覆盖的方法
alter 'student1', {NAME => 'f1', VERSIONS => 3}
#删除信息使用特殊的语法
alter 'student1', NAME => 'f1', METHOD => 'delete'
alter 'student1', 'delete' => 'f1'
===========删除表===========
#shell 中删除表格,需要先将表格状态设置为不可用
disable 'student1'
drop 'student1'
======================DML======================
#1)写入数据
#在 HBase 中如果想要写入数据,只能添加结构中最底层的 cell。可以手动写入时间戳指定 cell 的版本,推荐不写默认使用当前的系统时间。
#如果重复写入相同 rowKey,相同列的数据,会写入多个版本进行覆盖。
put 'bigdata:student','1001','info:name','zhangsan'
put 'bigdata:student','1001','info:name','lisi'
put 'bigdata:student','1001','info:age','18'
#2)读取数据
#读取数据的方法有两个:get 和 scan。
#get 最大范围是一行数据,也可以进行列的过滤,读取数据的结果为多行 cell。
get 'bigdata:student','1001'
get 'bigdata:student','1001' , {COLUMN => 'info:name'}
#也可以修改读取 cell 的版本数,默认读取一个。最多能够读取当前列族设置的维护版本数。
get 'bigdata:student','1001' , {COLUMN => 'info:name', VERSIONS => 6}
#scan 是扫描数据,能够读取多行数据,不建议扫描过多的数据,推荐使用 startRow 和stopRow 来控制读取的数据,默认范围左闭右开。
scan 'bigdata:student',{STARTROW => '1001',STOPROW => '1002'}
#3)删除数据
#删除数据的方法有两个:delete 和 deleteall。
#delete 表示删除一个版本的数据,即为 1 个 cell,不填写版本默认删除最新的一个版本。
delete 'bigdata:student','1001','info:name'
#deleteall 表示删除所有版本的数据,即为当前行当前列的多个 cell。(执行命令会标记数据为要删除,不会直接将数据彻底删除,删除数据只在特定时期清理磁盘时进行)
deleteall 'bigdata:student','1001','info:name'
```
##### **HBase API**
###### HBaseConnect
```java
package com.cfl;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
/**
* HBase 的客户端连接由 ConnectionFactory 类来创建,用户使用完成
* 之后需要手动关闭连接。同时连接是一个重量级的,推荐一个进程使用一个连接,对 HBase
* 的命令通过连接中的两个属性 Admin 和 Table 来实现。
*/
@Slf4j
public class HBaseConnect {
// 设置静态属性 hbase 连接
public static Connection connection = null;
static {
// 创建 hbase 的连接
try {
// 使用配置文件的方法
// 1. 创建配置对象
Configuration conf = new Configuration();
// 2. 添加配置参数
conf.set("hbase.zookeeper.quorum", "127.0.0.1");
// 3. 创建 hbase 的连接
// 默认使用同步连接
connection = ConnectionFactory.createConnection(conf);
log.info("connection=>{}",connection);
// 可以使用异步连接
// 主要影响后续的 DML 操作
//CompletableFuture
2)生成 16 进制序列预分区
create 'staff2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
3)按照文件中设置的规则预分区
(1)创建 splits.txt 文件内容如下:
aaaa
bbbb
ccccc
(2)然后执行:create 'staff3', 'info',SPLITS_FILE => 'splits.txt'
4)使用 JavaAPI 创建预分区
java
package com.atguigu.hbase;
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.util.Bytes;
import java.io.IOException;
public class HBaseConnect {
public static void main(String[] args) throws IOException {
// 1.获取配置类
Configuration conf = HBaseConfiguration.create();
// 2.给配置类添加配置
conf.set("hbase.zookeeper.quorum","hadoop102,hadoop103,hadoop104"
);
// 3.获取连接
Connection connection =
ConnectionFactory.createConnection(conf);
// 3.获取 admin
Admin admin = connection.getAdmin();
// 5.获取 descriptor 的 builder
TableDescriptorBuilder builder =
TableDescriptorBuilder.newBuilder(TableName.valueOf("bigdata",
"staff4"));
// 6. 添加列族
builder.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(
Bytes.toBytes("info")).build());
// 7.创建对应的切分
byte[][] splits = new byte[3][];
splits[0] = Bytes.toBytes("aaa");
splits[1] = Bytes.toBytes("bbb");
splits[2] = Bytes.toBytes("ccc");
// 8.创建表
admin.createTable(builder.build(),splits);
// 9.关闭资源
admin.close();
connection.close();
}
}
系统拆分
Region 的拆分是由 HRegionServer 完成的,在操作之前需要通过 ZK 汇报 master,修改对应的 Meta 表信息添加两列 info:splitA 和 info:splitB 信息。之后需要操作 HDFS 上面对应的文件,按照拆分后的 Region 范围进行标记区分,实际操作为创建文件引用,不会挪动数据。刚完成拆分的时候,两个 Region 都由原先的 RegionServer 管理。之后汇报给 Master,由Master将修改后的信息写入到Meta表中。等待下一次触发负载均衡机制,才会修改Region
的管理服务者,而数据要等到下一次压缩时,才会实际进行移动。
不管是否使用预分区,系统都会默认启动一套 Region 拆分规则。不同版本的拆分规则有差别。系统拆分策略的父类为 RegionSplitPolicy。
0.94 版本之前 => ConstantSizeRegionSplitPolicy
( 1 ) 当 1 个 region 中 的 某 个 Store 下 所 有 StoreFile 的 总 大 小 超 过
hbase.hregion.max.filesize (10G),该 Region 就会进行拆分。
0.94 版本之后,2.0 版本之前 => IncreasingToUpperBoundRegionSplitPolicy
( 2 ) 当 1 个 region 中 的 某 个 Store 下 所 有 StoreFile 的 总 大 小 超 过
Min(initialSize*R^3 ,hbase.hregion.max.filesize"),该 Region 就会进行拆分。其中 initialSize 的
默认值为 2*hbase.hregion.memstore.flush.size,R 为当前 Region Server 中属于该 Table 的
Region 个数(0.94 版本之后)。
具体的切分策略为:
第一次 split:1^3 * 256 = 256MB
第二次 split:2^3 * 256 = 2048MB
第三次 split:3^3 * 256 = 6912MB
第四次 split:4^3 * 256 = 16384MB > 10GB,因此取较小的值 10GB
后面每次 split 的 size 都是 10GB 了。
2.0 版本之后 => SteppingSplitPolicy
(3)Hbase 2.0 引入了新的 split 策略:如果当前 RegionServer 上该表只有一个 Region,
按照 2 * hbase.hregion.memstore.flush.size 分裂,否则按照 hbase.hregion.max.filesize 分裂。
这叫大道至简,学海抽丝
HBase 优化
RowKey 设计
一条数据的唯一标识就是 rowkey,那么这条数据存储于哪个分区,取决于 rowkey 处于哪个一个预分区的区间内,设计 rowkey的主要目的 ,就是让数据均匀的分布于所有的 region中,在一定程度上防止数据倾斜。接下来我们就谈一谈 rowkey 常用的设计方案。
1)生成随机数、hash、散列值
2)时间戳反转
3)字符串拼接
**需求:**使用 hbase 存储下列数据,要求能够通过 hbase 的 API 读取数据完成两个统计需求。
(1)统计张三在 2021 年 12 月份消费的总金额
(2)统计所有人在 2021 年 12 月份消费的总金额
为了能够统计张三在 2021 年 12 月份消费的总金额,我们需要用 scan 命令能够得到张
三在这个月消费的所有记录,之后在进行累加即可。Scan 需要填写 startRow 和 stopRow:
scan :startRow -> AAzhangsan2021-12
endRow -> AAzhangsan2021-12
注意点:
1)避免扫描数据混乱,解决字段长度不一致的问题,可以使用相同阿斯卡码值的符
号进行填充,框架底层填充使用的是阿斯卡码值为 1 的^A。最终得到 rowKey 的设计为:
rowKey: userdate(yyyy-MM-dd HH:mm:SS)
2) rowkey 相同的数据会视为相同数据覆盖掉之前的版本
**问题提出:**按照上面的 rowKey 设计,会发现对于需求 2,完全没有办法写 rowKey 的扫描范围。此处能够看出 hbase 设计 rowKey 使用的特点为:
调整的原则为:可枚举的放在前面。其中时间是可以枚举的,用户名称无法枚举,所以适用性强、泛用性差,能够完美实现一个需求但是不能同时完美实现多个需要。
如果想要同时完成两个需求,需要对 rowKey 出现字段的顺序进行调整。必须把时间放在前面。
最终满足 2 个需求的设计可以穷举的写在前面即可
rowKey 设计格式 => date(yyyy-MM)AAuserdate(-dd hh:mm:ss ms)
(1)统计张三在 2021 年 12 月份消费的总金额
scan: startRow => 2021-12AAzhangsan
stopRow => 2021-12AAzhangsan.
(2)统计所有人在 2021 年 12 月份消费的总金额
scan: startRow => 2021-12
stopRow => 2021-12.
添加预分区优化:
预分区的分区号同样需要遵守 rowKey 的 scan 原则。所有必须添加在 rowKey 的最前面,前缀为最简单的数字。同时使用 hash 算法将用户名和月份拼接决定分区号。(单独使用用户名会造成单一用户所有数据存储在一个分区)
添加预分区优化
startKey stopKey
000 001
001 002
002 003
...
119 120
分区号=> hash(user+date(MM)) % 120
分区号填充 如果得到 1 => 001
rowKey 设计格式 => 分区号 date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)
缺点:实现需求 2 的时候,由于每个分区都有 12 月份的数据,需要扫描 120 个分区。
解决方法:提前将分区号和月份进行对应。
000 到 009 分区 存储的都是 1 月份数据
010 到 019 分区 存储的都是 2 月份数据
...
110 到 119 分区 存储的都是 12 月份数据
是 9 月份的数据
分区号=> hash(user+date(MM)) % 10 + 80
分区号填充 如果得到 85 => 085
得到 12 月份所有人的数据
扫描 10 次
scan: startRow => 1102021-12
stopRow => 1102021-12.
...
startRow => 1122021-12
stopRow => 1122021-12.
..
startRow => 1192021-12
stopRow => 1192021-12.
参数优化
1)Zookeeper 会话超时时间
hbase-site.xml
属性:zookeeper.session.timeout
解释:默认值为 90000 毫秒(90s)。当某个 RegionServer 挂掉,90s 之后 Master 才能察觉到。可适当减小此值,尽可能快地检测 regionserver 故障,可调整至 20-30s。看你能有都能忍耐超时,同时可以调整重试时间和重试次数
hbase.client.pause(默认值 100ms)
hbase.client.retries.number(默认 15 次)
2)设置 RPC 监听数量
hbase-site.xml
属性:hbase.regionserver.handler.count
解释:默认值为 30,用于指定 RPC 监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
3)手动控制 Major Compaction
hbase-site.xml
属性:hbase.hregion.majorcompaction
解释:默认值:604800000 秒(7 天), Major Compaction 的周期,若关闭自动 Major Compaction,可将其设为 0。如果关闭一定记得自己手动合并,因为大合并非常有意义
4)优化 HStore 文件大小
hbase-site.xml
属性:hbase.hregion.max.filesize
解释:默认值 10737418240(10GB),如果需要运行 HBase 的 MR 任务,可以减小此值,因为一个 region 对应一个 map 任务,如果单个 region 过大,会导致 map 任务执行时间过长。该值的意思就是,如果 HFile 的大小达到这个数值,则这个 region 会被切分为两个 Hfile。
5)优化 HBase 客户端缓存
hbase-site.xml
属性:hbase.client.write.buffer
解释:默认值 2097152bytes(2M)用于指定 HBase 客户端缓存,增大该值可以减少 RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少 RPC 次数的目的。
6)指定 scan.next 扫描 HBase 所获取的行数
hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于指定 scan.next 方法获取的默认行数,值越大,消耗内存越大。
7)BlockCache 占用 RegionServer 堆内存的比例
hbase-site.xml
属性:hfile.block.cache.size
解释:默认 0.4,读请求比较多的情况下,可适当调大
8)MemStore 占用 RegionServer 堆内存的比例
hbase-site.xml
属性:hbase.regionserver.global.memstore.size
解释:默认 0.4,写请求较多的情况下,可适当调大Lars Hofhansl(拉斯·霍夫汉斯)大神推荐 Region 设置 20G,刷写大小设置 128M,其它默认。
JVM 调优
JVM 调优的思路有两部分:一是内存设置,二是垃圾回收器设置。
垃圾回收的修改是使用并发垃圾回收,默认 PO+PS 是并行垃圾回收,会有大量的暂停。
理由是 HBsae 大量使用内存用于存储数据,容易遭遇数据洪峰造成 OOM,同时写缓存的数据是不能垃圾回收的,主要回收的就是读缓存,而读缓存垃圾回收不影响性能,所以最终设置的效果可以总结为:防患于未然,早洗早轻松。
1)设置使用CMS 收集器:
-XX:+UseConcMarkSweepGC
2)保持新生代尽量小,同时尽早开启 GC,例如:
//在内存占用到 70%的时候开启 GC
-XX:CMSInitiatingOccupancyFraction=70
//指定使用 70%,不让 JVM 动态调整
-XX:+UseCMSInitiatingOccupancyOnly
//新生代内存设置为 512m
-Xmn512m
//并行执行新生代垃圾回收
-XX:+UseParNewGC
// 设 置 scanner 扫 描 结 果 占 用 内 存 大 小 , 在 hbase-site.xml 中,设置
hbase.client.scanner.max.result.size(默认值为 2M)为 eden 空间的 1/8(大概在 64M)
// 设置多个与 max.result.size * handler.count 相乘的结果小于 Survivor Space(新生代经过垃圾回收之后存活的对象)
使用法则
官方给出了权威的使用法则:
(1)Region 大小控制 10-50G
(2)cell 大小不超过 10M(性能对应小于 100K 的值有优化),如果使用 mob(Medium sized Objects 一种特殊用法)则不超过 50M。
(3)1 张表有 1 到 3 个列族,不要设计太多。最好就 1 个,如果使用多个尽量保证不会同时读取多个列族。
(4)1 到 2 个列族的表格,设计 50-100 个 Region。
(5)列族名称要尽量短,不要去模仿 RDBMS(关系型数据库)具有准确的名称和描述。
(6)如果 RowKey 设计时间在最前面,会导致有大量的旧数据存储在不活跃的 Region中,使用的时候,仅仅会操作少数的活动 Region,此时建议增加更多的 Region 个数。
(7)如果只有一个列族用于写入数据,分配内存资源的时候可以做出调整,即写缓存不会占用太多的内存。
集成 Phoenix
Phoenix 介绍
Phoenix 是 HBase 的开源 SQL 皮肤。可以使用标准 JDBC API 代替 HBase 客户端 API来创建表,插入数据和查询 HBase 数据
官方给的解释为:在 Client 和 HBase 之间放一个 Phoenix 中间层不会减慢速度,因为用户编写的数据处理代码和 Phoenix 编写的没有区别,不仅如此Phoenix 对于用户输入的 SQL 同样会有大量的优化手段(就像 hive 自带 sql 优化器一样)。
Phoenix 在 5.0 版本默认提供有两种客户端使用(瘦客户端和胖客户端),在 5.1.2 版本安装包中删除了瘦客户端,而胖客户端和用户自己写 HBase 的API 代码读取数据之后进行数据处理是完全一样的。
Phoenix 入门
Phoenix安装
略
Phoenix Shell
关于 Phoenix 的语法建议使用时直接查看官网:
https://phoenix.apache.org/language/index.html
sql
1)显示所有表
!table 或 !tables
2)创建表 (在 phoenix 中,表名等会自动转换为大写,若要小写,使用双引号,如"us_population"。)
-- 指定单个列作为 RowKey
CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,
name VARCHAR,
age BIGINT,
addr VARCHAR);
-- 指定多个列的联合作为 RowKey
CREATE TABLE IF NOT EXISTS student1 (
id VARCHAR NOT NULL,
name VARCHAR NOT NULL,
age BIGINT,
addr VARCHAR
CONSTRAINT my_pk PRIMARY KEY (id, name));
3)插入数据
upsert into student values('1001','zhangsan', 10, 'beijing');
4)查询记录
select * from student;
select * from student where id='1001';
5)删除记录
delete from student where id='1001';
6)删除表
drop table student;
7)退出命令行
!quit
注:Phoenix 中建表,会在 HBase 中创建一张对应的表。为了减少数据对磁盘空间的占用,Phoenix 默认会对HBase 中的列名做编码处理。具体规则可参考官网链接:https://phoenix.apache.org/columnencoding.html,
若不想对列名编码,可在建表语句末尾加上 COLUMN_ENCODED_BYTES = 0;
Phoenix JDBC
此处演示一个标准的 JDBC 连接操作,实际开发中会直接使用别的框架内嵌的 Phoenix 连接。
(1)maven 依赖
yml
<dependencies>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-client-hbase-2.4</artifactId>
<version>5.1.2</version>
</dependency>
</dependencies>
(2)编写代码
java
package com.atguigu.phoenix;
import java.sql.*;
import java.util.Properties;
public class PhoenixClient {
public static void main(String[] args) throws SQLException {
// 标准的 JDBC 代码
// 1.添加链接
String url =
"jdbc:phoenix:hadoop102,hadoop103,hadoop104:2181";
// 2. 创建配置
// 没有需要添加的必要配置 因为 Phoenix 没有账号密码
Properties properties = new Properties();
// 3. 获取连接
Connection connection = DriverManager.getConnection(url, properties);
// 5.编译 SQL 语句
PreparedStatement preparedStatement =
connection.prepareStatement("select * from student");
// 6.执行语句
ResultSet resultSet = preparedStatement.executeQuery();
// 7.输出结果
while (resultSet.next()){
System.out.println(resultSet.getString(1) + ":" +
resultSet.getString(2) + ":" + resultSet.getString(3));
}
// 8.关闭资源
connection.close();
// 由于 Phoenix 框架内部需要获取一个 HBase 连接,所以会延迟关闭
// 不影响后续的代码执行
System.out.println("hello");
}
}
Phoenix 索引
二级索引
配置文件,添加如下配置到 HBase 的 HRegionserver 节点的 hbase-site.xml。
<!-- phoenix regionserver 配置参数-->
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
全局索引
Global Index 是默认的索引格式,创建全局索引时,会在 HBase 中建立一张新表。也就是说索引数据和数据表是存放在不同的表中的,因此全局索引适用于多读少写的业务场景。
写数据的时候会消耗大量开销,因为索引表也要更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。
创建单个字段的全局索引。
CREATE INDEX my_index ON my_table (my_col);
#例如
create index my_index on student1(age);
#删除索引
DROP INDEX my_index ON my_table
drop index my_index on student1;
查看二级索引是否有效,可以使用 explainPlan 执行计划,有二级索引之后会变成范围扫描
explain select id,name from student1 where age = 10;
如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的提升。
若想解决上述问题,可采用如下方案:
(1)使用包含索引
(2)使用本地索引
包含索引
创建携带其他字段的全局索引(本质还是全局索引)。
CREATE INDEX my_index ON my_table (v1) INCLUDE (v2);
先删除之前的索引:
drop index my_index on student1;
#创建包含索引
create index my_index on student1(age) include (addr);
之后使用执行计划查看效果
explain select id,name,addr from student1 where age = 10;
本地索引
Local Index 适用于写操作频繁的场景。
索引数据和数据表的数据是存放在同一张表中(且是同一个 Region),避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。
my_column 可以是多个。
CREATE LOCAL INDEX my_index ON my_table (my_column);
本地索引会将所有的信息存在一个影子列族中,虽然读取的时候也是范围扫描,但是没有全局索引快,优点在于不用写多个表了。
#删除之前的索引
drop index my_index on student1;
#创建本地索引
CREATE LOCAL INDEX my_index ON student1 (age,addr);
#使用执行计划
explain select id,name,addr from student1 where age = 10;
集成 Hive
如果大量的数据已经存放在 HBase 上面,需要对已经存在的数据进行数据分析处理,那么 Phoenix 并不适合做特别复杂的 SQL 处理,此时可以使用 hive 映射 HBase 的表格,之后写 HQL 进行分析处理
在 hive-site.xml 中添加 zookeeper 的属性,如下:
<property>
<name>hive.zookeeper.quorum</name>
<value>hadoop102,hadoop103,hadoop104</value>
</property>
<property>
<name>hive.zookeeper.client.port</name>
<value>2181</value>
</property>
案例一
**目标:**建立 Hive 表,关联 HBase 表,插入数据到 Hive 表的同时能够影响 HBase 表。
(1)在 Hive 中创建表同时关联 HBase
CREATE TABLE hive_hbase_emp_table(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" =
":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:co
mm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
提示:完成之后,可以分别进入 Hive 和 HBase 查看,都生成了对应的表。
(2)在 Hive 中创建临时中间表,用于 load 文件中的数据
提示:不能将数据直接 load 进 Hive 所关联 HBase 的那张表中。
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int
)
row format delimited fields terminated by '\t';
(3)向 Hive 中间表中 load 数据
hive> load data local inpath '/opt/software/emp.txt' into table emp;
(4)通过 insert 命令将中间表中的数据导入到 Hive 关联 Hbase 的那张表中
hive> insert into table hive_hbase_emp_table select * from emp;
(5)查看 Hive 以及关联的 HBase 表中是否已经成功的同步插入了数据
Hive:hive> select * from hive_hbase_emp_table;
HBase:Hbase> scan 'hbase_emp_table'
案例二
**目标:**在 HBase 中已经存储了某一张表 hbase_emp_table,然后在 Hive 中创建一个外部表来关联 HBase 中的 hbase_emp_table 这张表,使之可以借助 Hive 来分析 HBase 这张表中的数据。
**注:**该案例 2 紧跟案例 1 的脚步,所以完成此案例前,请先完成案例 1。
(1)在 Hive 中创建外部表
CREATE EXTERNAL TABLE relevance_hbase_emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int
)
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" =
":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:co
mm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
(2)关联后就可以使用 Hive 函数进行一些分析操作了
hive (default)> select deptno,avg(sal) monery from relevance_hbase_emp group by deptno ;