校园超市网站开发背景,搭建服务器多少钱,python 开发手机app,做网站的税率文章目录 HBase简介HBase定义HBase数据模型**HBase** **逻辑结构****HBase** **物理存储结构****HBase** **基本架构** HBase 入门**HBase** **安装部署****HBase** 配置文件**HBase** 启动停止**HBase** **访问页面****HBase** **高可用****HBase Shell****HBase API**HBaseCo… 文章目录 HBase简介HBase定义HBase数据模型**HBase** **逻辑结构****HBase** **物理存储结构****HBase** **基本架构** HBase 入门**HBase** **安装部署****HBase** 配置文件**HBase** 启动停止**HBase** **访问页面****HBase** **高可用****HBase Shell****HBase API**HBaseConnectHBaseDDLHBaseDML **HBase** **进阶****Master** **架构****RegionServer** **架构**HBase写流程MemStore Flush HBase读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区自定义分区系统拆分 HBase 优化RowKey 设计参数优化JVM 调优使用法则 集成 PhoenixPhoenix 介绍Phoenix 入门Phoenix安装Phoenix ShellPhoenix JDBC Phoenix 索引二级索引全局索引包含索引本地索引 集成 Hive案例一案例二 HBase简介
HBase定义
Apache HBase 是以 hdfs 为数据存储的一种分布式、可扩展的 NoSQL 数据库
HBase数据模型
HBase 的设计理念依据 Google 的 BigTable 论文论文中对于数据模型的首句介绍。
Bigtable 是一个稀疏的、分布式的、持久的多维排序 map。之后对于映射的解释如下
该映射由行键、列键和时间戳索引映射中的每个值都是一个未解释的字节数组。
最终 HBase 关于数据模型和 BigTable 的对应关系如下
HBase 使用与 Bigtable 非常相似的数据模型。用户将数据行存储在带标签的表中。
数据行具有可排序的键和任意数量的列。
该表存储稀疏因此如果用户喜欢同一表中的行可以具有疯狂变化的列。
最终理解 HBase 数据模型的关键在于稀疏、分布式、多维、排序的映射。
其中映射 map指代非关系型数据库的 key-Value 结构。
1Name Space
命名空间类似于关系型数据库的 database 概念每个命名空间下有多个表。HBase 两个自带的命名空间分别是 hbase 和 defaulthbase 中存放的是 HBase 内置的表default表是用户默认使用的命名空间。
2Table
类似于关系型数据库的表概念。不同的是HBase 定义表时只需要声明列族即可不需要声明具体的列。因为数据存储时稀疏的所有往 HBase 写入数据时字段可以动态、按需指定。因此和关系型数据库相比HBase 能够轻松应对字段变更的场景。
3Row
HBase 表中的每行数据都由一个 RowKey 和多个 Column列组成数据是按照 RowKey的字典顺序存储的并且查询数据时只能根据 RowKey 进行检索所以 RowKey 的设计十分重要。
4Column
HBase 中的每个列都由 Column Family(列族)和 Column Qualifier列限定符进行限定例如 infonameinfoage。建表时只需指明列族而列限定符无需预先定义。
5Time Stamp
用于标识数据的不同版本version每条数据写入时系统会自动为其加上该字段其值为写入 HBase 的时间。
6Cell
由{rowkey, column Familycolumn Qualifier, timestamp} 唯一确定的单元。
cell 中的数据全部是字节码形式存贮。
HBase 逻辑结构
存储数据稀疏数据存储多维不同的行具有不同的列。
数据存储整体有序按照RowKey的字典序排列RowKey为Byte数组 HBase 物理存储结构
物理存储结构即为数据映射关系而在概念视图的空单元格底层实际根本不存储。 HBase 基本架构
Master
主要进程具体实现类为HMaster通常部署在namenode上。
功能负责通过ZK监控RegionServer进程状态同时是所有元数据变化的接口。内部启动监控执行region的故障转移和拆分的线程。
RegionServer
主要进程具体实现类为HRegionServer部署在datanode上。
功能主要负责数据cell的处理。同时在执行区域的拆分和合并的时候由RegionServer来实际执行。 架构角色
1Master
实现类为 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读取日志继续干。
2Region Server
Region Server 实现类为 HRegionServer主要作用如下:
1负责数据 cell 的处理例如写入数据 put查询数据 get 等
2拆分合并 region 的实际执行者有 master 监控有 regionServer 执行。
3Zookeeper
HBase 通过 Zookeeper 来做 master 的高可用、记录 RegionServer 的部署信息、并且存储有 meta 表的位置信息。
HBase 对于数据的读写操作时直接访问 Zookeeper 的在 2.3 版本推出 Master Registry模式客户端可以直接访问 master。使用此功能会加大对 master 的压力减轻对 Zookeeper的压力。
4HDFS
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 集群如果没有开启则跳过此步
[atguiguhadoop102 hbase]$ bin/stop-hbase.sh
2在 conf 目录下创建 backup-masters 文件
[atguiguhadoop102 hbase]$ touch conf/backup-masters
3在backup-masters 文件中配置高可用 HMaster 节点
[atguiguhadoop102 hbase]$ echo hadoop103 conf/backup-masters
4将整个 conf 目录 scp 到其他节点
[atguiguhadoop102 hbase]$ xsync conf
5重启 hbase,打开页面测试查看
HBase Shell
#进入 HBase 客户端命令行
bin/hbase shell
#特定的 help 语法能够查看命令如何使用
help create_namespace
#创建命名空间 bigdata
create_namespace bigdata
#查看所有的命名空间
list_namespaceDDL
#在 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 student1DML
#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:nameHBase API
HBaseConnect
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 操作//CompletableFutureAsyncConnection asyncConnection ConnectionFactory.createConnection(conf);} catch (IOException e) {System.out.println(连接获取失败);e.printStackTrace();}}/*** 连接关闭方法,用于进程关闭时调用** throws IOException*/public static void closeConnection() throws IOException {if (connection ! null) {connection.close();}}public static void main(String[] args) throws IOException {// 4. 使用连接//HBaseDDL.createNamespace(bigdata);//HBaseDDL.createTable(bigdata,student,info);//HBaseDML.putCell(bigdata, student, 1002, info, name, lisi);HBaseDML.getCells(bigdata, student, 1001, info, name);//HBaseDML.getCells(bigdata, student, 1002, info, name);//HBaseDML.scanRows(bigdata, student, 1001, 2000);//HBaseDML.deleteColumn(bigdata, student, 1001, info, name);// 5. 关闭连接closeConnection();}}HBaseDDL
package com.cfl;import org.apache.hadoop.hbase.NamespaceDescriptor;
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 HBaseDDL {// 添加静态属性 connection 指向单例连接public static Connection connection HBaseConnect.connection;/* 创建命名空间* param namespace 命名空间名称*/public static void createNamespace(String namespace) throws IOException {// 1. 获取 admin// 此处的异常先不要抛出 等待方法写完 再统一进行处理// admin 的连接是轻量级的 不是线程安全的 不推荐池化或者缓存这个连接Admin admin connection.getAdmin();// 2. 调用方法创建命名空间// 代码相对 shell 更加底层 所以 shell 能够实现的功能 代码一定能实现// 所以需要填写完整的命名空间描述// 2.1 创建命令空间描述建造者 设计师NamespaceDescriptor.Builder builder NamespaceDescriptor.create(namespace);// 2.2 给命令空间添加需求builder.addConfiguration(user, cfl);// 2.3 使用 builder 构造出对应的添加完参数的对象 完成创建// 创建命名空间出现的问题 都属于本方法自身的问题 不应该抛出try {admin.createNamespace(builder.build());} catch (IOException e) {System.out.println(命令空间已经存在);e.printStackTrace();}// 3. 关闭 adminadmin.close();}/*** 判断表格是否存在** param namespace 命名空间名称* param tableName 表格名称* return ture 表示存在*/public static boolean isTableExists(String namespace, StringtableName) throws IOException {// 1. 获取 adminAdmin admin connection.getAdmin();// 2. 使用方法判断表格是否存在boolean b false;try {b admin.tableExists(TableName.valueOf(namespace,tableName));} catch (IOException e) {e.printStackTrace();}// 3. 关闭 adminadmin.close();// 3. 返回结果return b;// 后面的代码不能生效}/* 创建表格* param namespace 命名空间名称* param tableName 表格名称* param columnFamilies 列族名称 可以有多个*/public static void createTable(String namespace, StringtableName, String... columnFamilies) throws IOException {// 判断是否有至少一个列族if (columnFamilies.length 0) {System.out.println(创建表格至少有一个列族);return;}// 判断表格是否存在if (isTableExists(namespace, tableName)) {System.out.println(表格已经存在);return;}// 1.获取 adminAdmin admin connection.getAdmin();// 2. 调用方法创建表格// 2.1 创建表格描述的建造者TableDescriptorBuilder tableDescriptorBuilder TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace,tableName));// 2.2 添加参数for (String columnFamily : columnFamilies) {// 2.3 创建列族描述的建造者ColumnFamilyDescriptorBuildercolumnFamilyDescriptorBuilder ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));// 2.4 对应当前的列族添加参数// 添加版本参数columnFamilyDescriptorBuilder.setMaxVersions(5);// 2.5 创建添加完参数的列族描述tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build());}// 2.6 创建对应的表格描述try {admin.createTable(tableDescriptorBuilder.build());} catch (IOException e) {e.printStackTrace();}// 3. 关闭 adminadmin.close();}/*** 修改表格中一个列族的版本* param namespace 命名空间名称* param tableName 表格名称* param columnFamily 列族名称* param version 版本*/public static void modifyTable(String namespace ,StringtableName,String columnFamily,int version) throws IOException {// 判断表格是否存在if (!isTableExists(namespace,tableName)){System.out.println(表格不存在无法修改);return;}// 1. 获取 adminAdmin admin connection.getAdmin();try {// 2. 调用方法修改表格// 2.0 获取之前的表格描述TableDescriptor descriptor admin.getDescriptor(TableName.valueOf(namespace, tableName));// 2.1 创建一个表格描述建造者// 如果使用填写 tableName 的方法 相当于创建了一个新的表格描述建造者 没有之前的信息// 如果想要修改之前的信息 必须调用方法填写一个旧的表格描述TableDescriptorBuilder tableDescriptorBuilder TableDescriptorBuilder.newBuilder(descriptor);// 2.2 对应建造者进行表格数据的修改ColumnFamilyDescriptor columnFamily1 descriptor.getColumnFamily(Bytes.toBytes(columnFamily));// 创建列族描述建造者// 需要填写旧的列族描述ColumnFamilyDescriptorBuildercolumnFamilyDescriptorBuilder ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);// 修改对应的版本columnFamilyDescriptorBuilder.setMaxVersions(version);// 此处修改的时候 如果填写的新创建 那么别的参数会初始化tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());admin.modifyTable(tableDescriptorBuilder.build());} catch (IOException e) {e.printStackTrace();}// 3. 关闭 adminadmin.close();}/* 删除表格* param namespace 命名空间名称* param tableName 表格名称* return true 表示删除成功*/public static boolean deleteTable(String namespace ,StringtableName) throws IOException {// 1. 判断表格是否存在if (!isTableExists(namespace,tableName)){System.out.println(表格不存在 无法删除);return false;}// 2. 获取 adminAdmin admin connection.getAdmin();// 3. 调用相关的方法删除表格try {// HBase 删除表格之前 一定要先标记表格为不可以TableName tableName1 TableName.valueOf(namespace,tableName);admin.disableTable(tableName1);admin.deleteTable(tableName1);} catch (IOException e) {e.printStackTrace();}// 4. 关闭 adminadmin.close();return true;}}
HBaseDML
package com.cfl;import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.ColumnValueFilter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;import java.io.IOException;
import java.util.List;public class HBaseDML {// 添加静态属性 connection 指向单例连接public static Connection connection HBaseConnect.connection;/*** 插入数据** param namespace 命名空间名称* param tableName 表格名称* param rowKey 主键* param columnFamily 列族名称* param columnName 列名* param value 值*/public static void putCell(String namespace, StringtableName, String rowKey, String columnFamily, StringcolumnName, String value) throws IOException {// 1. 获取 tableTable table connection.getTable(TableName.valueOf(namespace, tableName));// 2. 调用相关方法插入数据// 2.1 创建 put 对象Put put new Put(Bytes.toBytes(rowKey));// 2.2. 给 put 对象添加数据put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnName), Bytes.toBytes(value));// 2.3 将对象写入对应的方法try {table.put(put);} catch (IOException e) {e.printStackTrace();}// 3. 关闭 tabletable.close();}/*** 读取数据 读取对应的一行中的某一列** param namespace 命名空间名称* param tableName 表格名称* param rowKey 主键* param columnFamily 列族名称* param columnName 列名*/public static void getCells(String namespace, String tableName,String rowKey, String columnFamily, String columnName) throwsIOException {// 1. 获取 tableTable table connection.getTable(TableName.valueOf(namespace, tableName));// 2. 创建 get 对象Get get new Get(Bytes.toBytes(rowKey));// 如果直接调用 get 方法读取数据 此时读一整行数据// 如果想读取某一列的数据 需要添加对应的参数get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));// 设置读取数据的版本get.readAllVersions();try {// 读取数据 得到 result 对象Result result table.get(get);// 处理数据Cell[] cells result.rawCells();// 测试方法: 直接把读取的数据打印到控制台// 如果是实际开发 需要再额外写方法 对应处理数据for (Cell cell : cells) {// cell 存储数据比较底层String value new String(CellUtil.cloneValue(cell));System.out.println(value);}} catch (IOException e) {e.printStackTrace();}// 关闭 tabletable.close();}/*** 扫描数据** param namespace 命名空间* param tableName 表格名称* param startRow 开始的 row 包含的* param stopRow 结束的 row 不包含*/public static void scanRows(String namespace, String tableName,String startRow, String stopRow) throws IOException {// 1. 获取 tableTable table connection.getTable(TableName.valueOf(namespace, tableName));// 2. 创建 scan 对象Scan scan new Scan();// 如果此时直接调用 会直接扫描整张表// 添加参数 来控制扫描的数据// 默认包含scan.withStartRow(Bytes.toBytes(startRow));// 默认不包含scan.withStopRow(Bytes.toBytes(stopRow));try {// 读取多行数据 获得 scannerResultScanner scanner table.getScanner(scan);// result 来记录一行数据 cell 数组// ResultScanner 来记录多行数据 result 的数组for (Result result : scanner) {Cell[] cells result.rawCells();for (Cell cell : cells) {System.out.print(newString(CellUtil.cloneRow(cell)) - newString(CellUtil.cloneFamily(cell)) - newString(CellUtil.cloneQualifier(cell)) - newString(CellUtil.cloneValue(cell)) \t);}System.out.println();}} catch (IOException e) {e.printStackTrace();}// 3. 关闭 tabletable.close();}/*** 带过滤的扫描** param namespace 命名空间* param tableName 表格名称* param startRow 开始 row* param stopRow 结束 row* param columnFamily 列族名称* param columnName 列名* param value value 值*/public static void filterScan(String namespace, String tableName,String startRow, String stopRow, String columnFamily, StringcolumnName, String value) throws IOException {// 1. 获取 tableTable table connection.getTable(TableName.valueOf(namespace, tableName));// 2. 创建 scan 对象Scan scan new Scan();// 如果此时直接调用 会直接扫描整张表// 添加参数 来控制扫描的数据// 默认包含scan.withStartRow(Bytes.toBytes(startRow));// 默认不包含scan.withStopRow(Bytes.toBytes(stopRow));// 可以添加多个过滤FilterList filterList new FilterList();// 创建过滤器// (1) 结果只保留当前列的数据ColumnValueFilter columnValueFilter new ColumnValueFilter(// 列族名称Bytes.toBytes(columnFamily),// 列名Bytes.toBytes(columnName),// 比较关系CompareOperator.EQUAL,// 值Bytes.toBytes(value));// (2) 结果保留整行数据// 结果同时会保留没有当前列的数据SingleColumnValueFilter singleColumnValueFilter newSingleColumnValueFilter(// 列族名称Bytes.toBytes(columnFamily),// 列名Bytes.toBytes(columnName),// 比较关系CompareOperator.EQUAL,// 值Bytes.toBytes(value));// 本身可以添加多个过滤器filterList.addFilter(singleColumnValueFilter);// 添加过滤scan.setFilter(filterList);try {// 读取多行数据 获得 scannerResultScanner scanner table.getScanner(scan);// result 来记录一行数据 cell 数组// ResultScanner 来记录多行数据 result 的数组for (Result result : scanner) {Cell[] cells result.rawCells();for (Cell cell : cells) {System.out.print(newString(CellUtil.cloneRow(cell)) - newString(CellUtil.cloneFamily(cell)) - newString(CellUtil.cloneQualifier(cell)) - newString(CellUtil.cloneValue(cell)) \t);}System.out.println();}} catch (IOException e) {e.printStackTrace();}// 3. 关闭 tabletable.close();}/*** 删除 column 数据** param nameSpace* param tableName* param rowKey* param family* param column* throws IOException*/public static void deleteColumn(String nameSpace, String tableName,String rowKey, String family, String column) throws IOException {// 1.获取 tableTable table connection.getTable(TableName.valueOf(nameSpace,tableName));// 2.创建 Delete 对象Delete delete new Delete(Bytes.toBytes(rowKey));// 3.添加删除信息// 3.1 删除单个版本
//delete.addColumn(Bytes.toBytes(family), Bytes.toBytes(column));// 3.2 删除所有版本delete.addColumns(Bytes.toBytes(family),Bytes.toBytes(column));// 3.3 删除列族
// delete.addFamily(Bytes.toBytes(family));// 3.删除数据table.delete(delete);// 5.关闭资源table.close();}}
HBase 进阶
Master 架构 1Meta表格介绍警告不要去改这个表
全称 hbasemeta只是在 list 命令中被过滤掉了本质上和 HBase 的其他表格一样。
RowKey([table],[region start key],[region id]) 即 表名region 起始位置和 regionID。
列
inforegioninfo 为 region 信息存储一个 HRegionInfo 对象。
infoserver 当前 region 所处的 RegionServer 信息包含端口号。
infoserverstartcode 当前 region 被分到 RegionServer 的起始时间。
如果一个表处于切分的过程中即 region 切分还会多出两列 infosplitA 和 infosplitB
存储值也是 HRegionInfo 对象拆分结束后删除这两列。
注意在客户端对元数据进行操作的时候才会连接 master如果对数据进行读写直接连接zookeeper 读取目录/hbase/meta-region-server 节点信息会记录 meta 表格的位置。直接读取即可不需要访问 master这样可以减轻 master 的压力相当于 master 专注 meta 表的写操作客户端可直接读取 meta 表。
在 HBase 的 2.3 版本更新了一种新模式Master Registry。客户端可以访问 master 来读取meta 表信息。加大了 master 的压力减轻了 zookeeper 的压力。
RegionServer 架构 1MemStore
写缓存由于 HFile 中的数据要求是有序的所以数据是先存储在 MemStore 中排好
序后等到达刷写时机才会刷写到 HFile每次刷写都会形成一个新的 HFile写入到对应的
文件夹 store 中。
2WAL
由于数据要经 MemStore 排序后才能刷写到 HFile但把数据保存在内存中会有很高的概率导致数据丢失为了解决这个问题数据会先写在一个叫做 Write-Ahead logfile 的文件中然后再写入 MemStore 中。所以在系统出现故障的时候数据可以通过这个日志文件重建。
3BlockCache
读缓存每次查询出的数据会缓存在 BlockCache 中方便下次查询。
HBase写流程 写流程顺序正如 API 编写顺序首先创建 HBase 的重量级连接
1首先访问 zookeeper获取 hbase:meta 表位于哪个 Region Server
2访问对应的 Region Server获取 hbase:meta 表将其缓存到连接中作为连接属性 MetaCache由于 Meta 表格具有一定的数据量导致了创建连接比较慢 之后使用创建的连接获取 Table这是一个轻量级的连接只有在第一次创建的时候会检查表格是否存在访问 RegionServer之后在获取 Table 时不会访问 RegionServer
3调用Table的put方法写入数据此时还需要解析RowKey对照缓存的MetaCache查看具体写入的位置有哪个 RegionServer
4将数据顺序写入追加到 WAL此处写入是直接落盘的并设置专门的线程控制 WAL 预写日志的滚动类似 Flume
5根据写入命令的 RowKey 和 ColumnFamily 查看具体写入到哪个 MemStory并且在 MemStory 中排序
6向客户端发送 ack
7 等达到 MemStore 的刷写时机后将数据刷写到对应的 story 中。
MemStore Flush
MemStore 刷写由多个线程控制条件互相独立
主要的刷写规则是控制刷写文件的大小在每一个刷写线程中都会进行监控
1当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size默认值 128M
其所在 region 的所有 memstore 都会刷写。
当 memstore 的大小达到了
hbase.hregion.memstore.flush.size默认值 128M
hbase.hregion.memstore.block.multiplier默认值 4
时会刷写同时阻止继续往该 memstore 写数据由于线程监控是周期性的所有有可能面对数据洪峰尽管可能性比较小
2由 HRegionServer 中的属性 MemStoreFlusher 内部线程 FlushHandler 控制。标准为LOWER_MARK低水位线和 HIGH_MARK高水位线意义在于避免写缓存使用过多的内存造成 OOM当 region server 中 memstore 的总大小达到低水位线
java_heapsize
hbase.regionserver.global.memstore.size默认值 0.4
hbase.regionserver.global.memstore.size.lower.limit默认值 0.95
region 会按照其所有 memstore 的大小顺序由大到小依次进行刷写。直到 region server中所有 memstore 的总大小减小到上述值以下。
当 region server 中 memstore 的总大小达到高水位线
java_heapsize
hbase.regionserver.global.memstore.size默认值 0.4
时会同时阻止继续往所有的 memstore 写数据。
3为了避免数据过长时间处于内存之中到达自动刷写的时间也会触发 memstore flush。由 HRegionServer 的属性 PeriodicMemStoreFlusher 控制进行由于重要性比较低5min才会执行一次。
自动刷新的时间间隔由该属性进行配置
hbase.regionserver.optionalcacheflushinterval默认1 小时。
4当 WAL 文件的数量超过 hbase.regionserver.max.logsregion 会按照时间顺序依次进行刷写直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下该属性名已经废弃现无需手动设置最大值为 32。
HBase读流程
在了解读流程之前需要先知道读取的数据是什么样子的。
HFile 结构
HFile 是存储在 HDFS 上面每一个 store 文件夹下实际存储数据的文件。里面存储多种内容。包括数据本身keyValue 键值对、元数据记录、文件信息、数据索引、元数据索引和一个固定长度的尾部信息记录文件的修改情况。键值对按照块大小默认 64K保存在文件中数据索引按照块创建块越多索引越大。每一个 HFile 还会维护一个布隆过滤器就像是一个很大的地图文件中每有一种 key就在对应的位置标记读取时可以大致判断要 get 的 key 是否存在 HFile 中。
KeyValue 内容如下:
rowlength -----------→ key 的长度
row -----------------→ key 的值
columnfamilylength --→ 列族长度
columnfamily --------→ 列族
columnqualifier -----→ 列名
timestamp -----------→ 时间戳默认系统时间
keytype -------------→ Put
由于 HFile 存储经过序列化所以无法直接查看。可以通过 HBase 提供的命令来查看存储在 HDFS 上面的 HFile 元数据内容。
bin/hbase hfile -m -f /hbase/data/命名空间/表名/regionID/列族/HFile 名读流程 创建连接同写流程。
1创建 Table 对象发送 get 请求。
2优先访问 Block Cache查找是否之前读取过并且可以读取 HFile 的索引信息和
布隆过滤器。
3不管读缓存中是否已经有数据了可能已经过期了都需要再次读取写缓存和
store 中的文件。
4最终将所有读取到的数据合并版本按照 get 的要求返回即可。
合并读取数据优化
每次读取数据都需要读取三个位置最后进行版本的合并。效率会非常低所有系统需
要对此优化。
1HFile 带有索引文件读取对应 RowKey 数据会比较快。
2Block Cache 会缓存之前读取的内容和元数据信息如果 HFile 没有发生变化记
录在 HFile 尾信息中则不需要再次读取。
3使用布隆过滤器能够快速过滤当前 HFile 不存在需要读取的 RowKey从而避免读
取文件。布隆过滤器使用 HASH 算法不是绝对准确的出错会造成多扫描一个文件对
读取数据结果没有影响
StoreFile Compaction 由于 memstore 每次刷写都会生成一个新的 HFile文件过多读取不方便所以会进行文件的合并清理掉过期和删除的数据会进行 StoreFile Compaction。
Compaction 分为两种分别是 Minor Compaction 和 Major Compaction。
Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile并清理掉部分过期和删除的数据有系统使用一组参数自动控制
Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile并且会清理掉所有过期和删除的数据由参数 hbase.hregion.majorcompaction控制默认 7 天。
Minor Compaction 控制机制
参与到小合并的文件需要通过参数计算得到有效的参数有 5 个
1hbase.hstore.compaction.ratio默认 1.2F合并文件选择算法中使用的比率。
2hbase.hstore.compaction.min默认 3 为 Minor Compaction 的最少文件个数。
3hbase.hstore.compaction.max默认 10 为 Minor Compaction 最大文件个数。
4hbase.hstore.compaction.min.size默认 128M为单个 Hfile 文件大小最小值小于这个数会被合并。
5hbase.hstore.compaction.max.size默认 Long.MAX_VALUE为单个 Hfile 文件大小最大值高于这个数不会被合并。
小合并机制为拉取整个 store 中的所有文件做成一个集合。之后按照从旧到新的顺序遍历。
判断条件为
① 过小合并过大不合并
② 文件大小/ hbase.hstore.compaction.ratio (剩余文件大小和) 则参与压缩。所有把比值设
置过大如 10 会最终合并为 1 个特别大的文件相反设置为 0.4会最终产生 4 个 storeFile。
不建议修改默认值
③ 满足压缩条件的文件个数达不到个数要求3 count 10则不压缩。
Region Split
Region 切分分为两种创建表格时候的预分区即自定义分区同时系统默认还会启动一个切分规则避免单个 Region 中的数据量太大。
预分区自定义分区
每一个 region 维护着 startRow 与 endRowKey如果加入的数据符合某个 region 维护的
rowKey 范围则该数据交给这个 region 维护。那么依照这个原则我们可以将数据所要投
放的分区提前大致的规划好以提高 HBase 性能。
1手动设定预分区
create ‘staff1’,‘info’, SPLITS [‘1000’,‘2000’,‘3000’,‘4000’]
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 创建预分区
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.获取 adminAdmin admin connection.getAdmin();// 5.获取 descriptor 的 builderTableDescriptorBuilder 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 表信息添加两列 infosplitA 和 infosplitB 信息。之后需要操作 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.sizeR 为当前 Region Server 中属于该 Table 的
Region 个数0.94 版本之后。
具体的切分策略为
第一次 split1^3 * 256 256MB
第二次 split2^3 * 256 2048MB
第三次 split3^3 * 256 6912MB
第四次 split4^3 * 256 16384MB 10GB因此取较小的值 10GB
后面每次 split 的 size 都是 10GB 了。
2.0 版本之后 SteppingSplitPolicy
3Hbase 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(userdate(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(userdate(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.参数优化
1Zookeeper 会话超时时间
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
解释默认值 1073741824010GB如果需要运行 HBase 的 MR 任务可以减小此值因为一个 region 对应一个 map 任务如果单个 region 过大会导致 map 任务执行时间过长。该值的意思就是如果 HFile 的大小达到这个数值则这个 region 会被切分为两个 Hfile。
5优化 HBase 客户端缓存
hbase-site.xml
属性hbase.client.write.buffer
解释默认值 2097152bytes2M用于指定 HBase 客户端缓存增大该值可以减少 RPC调用次数但是会消耗更多内存反之则反之。一般我们需要设定一定的缓存大小以达到减少 RPC 次数的目的。
6指定 scan.next 扫描 HBase 所获取的行数
hbase-site.xml
属性hbase.client.scanner.caching
解释用于指定 scan.next 方法获取的默认行数值越大消耗内存越大。
7BlockCache 占用 RegionServer 堆内存的比例
hbase-site.xml
属性hfile.block.cache.size
解释默认 0.4读请求比较多的情况下可适当调大
8MemStore 占用 RegionServer 堆内存的比例
hbase-site.xml
属性hbase.regionserver.global.memstore.size
解释默认 0.4写请求较多的情况下可适当调大Lars Hofhansl拉斯·霍夫汉斯大神推荐 Region 设置 20G刷写大小设置 128M其它默认。
JVM 调优
JVM 调优的思路有两部分一是内存设置二是垃圾回收器设置。
垃圾回收的修改是使用并发垃圾回收默认 POPS 是并行垃圾回收会有大量的暂停。
理由是 HBsae 大量使用内存用于存储数据容易遭遇数据洪峰造成 OOM同时写缓存的数据是不能垃圾回收的主要回收的就是读缓存而读缓存垃圾回收不影响性能所以最终设置的效果可以总结为防患于未然早洗早轻松。
1设置使用CMS 收集器
-XX:UseConcMarkSweepGC
2保持新生代尽量小同时尽早开启 GC例如
//在内存占用到 70%的时候开启 GC
-XX:CMSInitiatingOccupancyFraction70
//指定使用 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(新生代经过垃圾回收之后存活的对象)
使用法则
官方给出了权威的使用法则
1Region 大小控制 10-50G
2cell 大小不超过 10M性能对应小于 100K 的值有优化如果使用 mobMedium sized Objects 一种特殊用法则不超过 50M。
31 张表有 1 到 3 个列族不要设计太多。最好就 1 个如果使用多个尽量保证不会同时读取多个列族。
41 到 2 个列族的表格设计 50-100 个 Region。
5列族名称要尽量短不要去模仿 RDBMS关系型数据库具有准确的名称和描述。
6如果 RowKey 设计时间在最前面会导致有大量的旧数据存储在不活跃的 Region中使用的时候仅仅会操作少数的活动 Region此时建议增加更多的 Region 个数。
7如果只有一个列族用于写入数据分配内存资源的时候可以做出调整即写缓存不会占用太多的内存。
集成 Phoenix
Phoenix 介绍
http://phoenix.apache.org/
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
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 id1001;
5删除记录
delete from student where id1001;
6删除表
drop table student;
7退出命令行
!quit注Phoenix 中建表会在 HBase 中创建一张对应的表。为了减少数据对磁盘空间的占用Phoenix 默认会对HBase 中的列名做编码处理。具体规则可参考官网链接https://phoenix.apache.org/columnencoding.html
若不想对列名编码可在建表语句末尾加上 COLUMN_ENCODED_BYTES 0;
Phoenix JDBC
此处演示一个标准的 JDBC 连接操作实际开发中会直接使用别的框架内嵌的 Phoenix 连接。
1maven 依赖
dependenciesdependencygroupIdorg.apache.phoenix/groupIdartifactIdphoenix-client-hbase-2.4/artifactIdversion5.1.2/version/dependency
/dependencies2编写代码
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 配置参数--
propertynamehbase.regionserver.wal.codec/namevalueorg.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 的属性如下
propertynamehive.zookeeper.quorum/namevaluehadoop102,hadoop103,hadoop104/value
/propertypropertynamehive.zookeeper.client.port/namevalue2181/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 表中是否已经成功的同步插入了数据
Hivehive select * from hive_hbase_emp_table;
HBaseHbase 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 ;