文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、HBase的表结构设计
1.1 HBase的名称空间
HBase的名称空间类似于MySQL中的库
在HBase中, 默认提供了两个名称空间:
- 1- default: 默认的名称空间, 在创建表的时候, 如果不指定名称空间, 默认就会将表创建在这个default名称空间下
- 类似于在HIVE中有一个default库
- 2- hbase: 此名称空间是hbase专门用于放置hbase系统表的名称空间, 例如: meta 表就是存储在这个名称空间下, 此名称空间, 一般不使用的
# 创建名称空间
create_namespace '名称空间名字'
# 查看所有名称空间
list_namespace
# 在指定的名称空间下创建表
create '名称空间:表名','列族1','列族2'...
# 删除名称空间(删除名称空间的时候, 此名称空间下, 不能有表存在, 否则无法删除)
drop_namespace '名称空间'
# 查看某一个名称空间
describe_namespace '名称空间'
1.2 HBase的列族的设计
HBase在构建表时,列族建议越少越好,在某一些情况下可以构建多个列族
情况一:需要将一份数据存储到HBase的时候, 整个数据集中只有某几列数据经常使用到, 其他列的数据使用频次并不高,建议将其分为两个列族, 一个列族中用于放置经常使用的字段, 一个列族放置其他的字段
情况二: 一份数据集主要是用于对接两个不同的业务, 不同的业务的体系使用的数据集中不同的字段, 根据业务要求, 将不同业务数据划分到不同的列族中, 便于各个业务读取不同列族的下数据
1.3 Hbase的表压缩方案的选择
在HBase中,压缩的操作是存在与磁盘(HDFS)上,只有当数据落在HDFS上才会进行压缩,如果数据在内存中是不存在压缩的
如何选择压缩方案:
- 如果写入请求远远大于读取请求(写多读少),建议采用GZIP,保证在有限的空间下,存储更多的数据
- 如果读取请求远远大于写入数据的请求(读多写少),建议采用SNAPPY压缩方案,在保证一定的压缩率下,更好的提高压缩和解压的效率
配置压缩的格式:
# 新建表时设置压缩方案
create '表名',{NAME=>'列族',COMPERSSION=>'压缩算法'}
create 'test1',{NAME=>'C1', COMPERSSION=>'GZ'}
# 为已存在表添加压缩方案
alter '表名', {NAME => '列族', COMPRESSION => '压缩算法'}
alter 'test02' {NAME=>'C1',COMPRESSION=> 'GZ'}
注意: 在设置压缩方案的时候, 默认GZ是被支持的, 如果使用LZO 或者 SNAPPY , 有可能出现无法使用的情况, 如果非要尝试, 需要将HBase重新编译, 设置其支持其他的压缩方案, 以及增加相关的压缩方案的jar包, 或者 使用CDH版本的HBase
1.4 HBase的预分区
在HBase中默认情况下, 创建一个表只有一个Region, 只有一个Region也就意味着只能被一个RegionServer所管理
在此种情况下, 如果遇到大量的并发访问, 此时所有的请求全部打向同一个RegionServer上, 从而导致这个RegionServer承担更大的并发, 出现宕机的风险, 宕机后, 如果region被其他的RegionServer接管了, 可能还会接着宕机, 最终导致出现雪崩问题, 全部节点一起奔溃。
因此,引入预分区,让表的Region数量变得更多, 当Region变多了, 那么就可以让更多的RegionServer参与, 从而让更多RegionServer一起来承担并发读写请求
- 手动预分区(适合于对表的rowkey数据比较了解,可以大概预知分区后数据的平均分配情况)
create '表名','列族1','列族2'..., SPLITS=>[自定义分区方案] create 'test01','C1',SPLITS=>['10','20','30','40','50']
- Hash的预分区
create '表名','列族1','列族2'...,{NUMREGIONS => N, SPLITALGO => 'HexStringSplit'} create 'ns1:test01','C1',{NUMREGIONS=>5,SPLITALGO=>'HexStringSplit'}
不管采用哪种预分区的方式, 建议预分区的数量为RegionServer数量的2~5倍左右即可
1.5 HBase中rowkey的设计原则
通过预分区, 可以保证在创建表的时候, 就可以让表一开始拥有多个Region, 但是Region的划分方案是基于rowkey的范围来划分的, 如果说, rowkey在设计的过程中, 前缀都是以固定的名称来命名. 此时可能会导致所有的数据全部写入到某一个Region或者某几个Region上, 从而导致预分区设置没有效果的
- 官方设计原则:
1- 避免使用递增的行键/时序的数据作为rowkey的前缀 ( 不要以固定的前缀作为rowkey) 2- 在设计的时候rowkey和列的长度不能太长了, 建议越短越好, 一般建议在1~30区间,最长不能超过100字节 3- 使用数值类型的要比使用string更加节省空间 4- 保证rowkey的唯一性
- 业务原则
保证相关性的数据放置到同一个Region中 rowkey的设计能够满足一些固定的查询需求
如果rowkey的设计不良好,就会导致大量的数据集中到某一个或者几个Region中,而其他的Region中没有数据,或者数据极少。
解决方案
-
- 加盐处理(加随机数)
- 好处:基本保证数据落在不同的Region中
- 弊端:相关性无法保证,会将数据完全打散
-
- 反转的策略:手机号的反转,时间戳反转
- 好处:基本保证数据落在不同的Region中
- 弊端:相关性无法保证,会将数据完全打散
-
- hash处理–后续此种方式可以和Hash预分区配合使用, 基于HBase提供MD5HASH 方案生成rowkey的前缀(推荐)
- 好处:相关性的数据可以放置到一起
- 弊端:如果相关性的数据比较多,依然会导致热点问题的发生
1.6 HBase的版本确界和TTL
对于版本确界 和 TTL 在实际中需要根据实际生产需求进行设置, 如果生产环境中没有要求, 选择默认即可
1.6.1 版本的确界
所谓的版本的确界, 本质上指的就是是否需要保留历史版本, 以及保留多少个问题
- 下界: 指的至少需要保留多少个历史版本, 即使数据过期了, 也是需要保留的 默认为 0 (禁用)
- 上界: 指的最多需要保留多少个历史版本数据, 默认值: 1
1.6.2 数据TTL(数据保质期)
在HBase中可以对数据设置过期时间, 当达到时间后, 数据就会自动被删除掉了
1.6.3 代码演示版本确界和TTL
package com.itheima.hbase.ttl;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
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 HBaseVersionAndTTL {
public static void main(String[] args) throws IOException {
//1. 根据连接工厂, 创建HBase的连接对象
Configuration conf = HBaseConfiguration.create();
// conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
Connection HbaseConnect = ConnectionFactory.createConnection(conf);
//2. 根据连接对象, 获取相关管理对象
Admin admin = HbaseConnect.getAdmin();
// 2.1 判断某个表是否存在
if (!admin.tableExists(TableName.valueOf("VERSION_TTL"))) {
// 2.2 如果表不存在, 创建这个表
ColumnFamilyDescriptor columnFamily = ColumnFamilyDescriptorBuilder
.newBuilder("C1".getBytes())
.setMaxVersions(5)
.setMinVersions(3)
.setTimeToLive(80)
.build();
TableDescriptor version_ttl = TableDescriptorBuilder.newBuilder(TableName.valueOf("VERSION_TTL")).setColumnFamily(columnFamily).build();
admin.createTable(version_ttl);
}
// 2.3 如果表存在, 获取这个表对象
Table table = HbaseConnect.getTable(TableName.valueOf("VERSION_TTL"));
// 3. 执行相关操作: 添加数据(添加一条, 修改N次)
for (int i = 1; i <7 ; i++) {
Put put = new Put("rk0001".getBytes());
put.addColumn("C1".getBytes(),"NAME".getBytes(),("张三"+i).getBytes());
table.put(put);
}
// 4- 查看数据: 查看历史版本的数据
Get get = new Get("rk0001".getBytes());
// 设置读取所有的版本数据
get.readAllVersions();
Result result = table.get(get);
Cell[] cells = result.rawCells();// 获取所有版本的单元格信息
for (Cell cell : cells) {
System.out.println(Bytes.toString(CellUtil.cloneValue(cell)));
}
//5. 释放资源:
table.close();
admin.close();
HbaseConnect.close();
}
}
执行结果:
二、Phoenix的基本介绍
Phoenix是属于Apache旗下的一款顶级开源的基于HBase的工具,此工具提供一种权限的方式来操作HBase中数据(SQL),同时Phoenix对HBase进行大量的优化工作, 能够让我们更加有效的操作HBase
Phoenix的出现仅仅时为HBase提供了权限的方式, 并不是数据分析的引擎, 所以一般也不会使用Phoenix + HBase构建数仓, 传统的离线数仓 依然是基于Hadoop + HIVE
Phoenix更主要做的是一种即席查询
三、Phoenix的基本使用
3.1 如何在Phoenix中创建表
代码如下(示例):
# 创建一张订单表
create table order_dtl(
id varchar primary key,#primary key主键标识必须加
c1.status varchar,
c1.money integer,
c2.pay_way integer,
c1.user_id integer,
c2.operation_time varchar,
c2.category varchar);
默认情况下: Phoenix会将所有的小写的字段 表名 以及列族都变更为大写
一旦使用小写, 以后只要使用小写的内容, 必须使用双引号进行包裹, 所以一般建议使用大写, 而不是小写
3.2 查看所有的表
!table
3.3 查看某一个表结构信息
!desc 表名
3.4 向表中插入数据(修改数据)
#添加数据
upsert into order_dtl(id,status,money,pay_way,user_id,operation_time,category) values('000001','已提交',4070,1,4944191,'2020-04-25 12:09:16','手机;');
#修改操作:
upsert into order_dtl(id,category) values('000001','电脑;')
-
查询操作: 与标准的SQL是一致的
- 仅支持单表的操作, 不支持子查询, 也不支持Join的操作
-
删除数据: 与标准的SQL是一致的
-
删除表: 与标准的SQL是一致的
总结
使用phoenix就是将Hbase添加可以使用sql进行操作的过程,javaAPI也可以通过实现JDBC接口连接。