HBase
注:大家觉得博客好的话,别忘了点赞收藏呀,本人每周都会更新关于人工智能和大数据相关的内容,内容多为原创,Python Java Scala SQL 代码,CV NLP 推荐系统等,Spark Flink Kafka Hbase Hive Flume等等~写的都是纯干货,各种顶会的论文解读,一起进步。
今天继续和大家分享一下HBase
#博学谷IT学习技术支持
文章目录
前言
接着上次未完成的内容,继续分享HBase
一、HBase的高级Shell操作
1.HBase的过滤器查询操作
格式:
scan ‘表名’,{FILTER=>“过滤器名称(比较运算符,比较器表达式)” }
常见的过滤器:
rowkey过滤器:
RowFilter : 实现行键字符串的比较和过滤操作
PrefixFilter : rowkey 前缀过滤器
列族过滤器:
FamilyFilter: 列族过滤器
列名过滤器:
QualifierFilter : 列名过滤器, 中显示对应列名的数据
列值过滤器:
ValueFilter: 列值过滤器, 找到符合值的数据
SingleColumnValueFilter: 在执行的列族和列名中进行比较具体的值, 将符合条数据整行数据全部都返回(包含条件的内容字段)
SingleColumnValueExcludeFilter: 在执行的列族和列名中进行比较具体的值, 将符合条数据整行数据全部都返回(去除条件内容的字段)
其他过滤器:
PageFilter : 用来执行分页操作
比较运算符 : = > < >= <= !=
比较器:
BinaryComparator : 完整匹配字节数组
BinaryPrefixComparator : 匹配字节数组前缀
NullComparator : 匹配Null
SubstringComparator : 模糊匹配字符串
比较器表达式:
BinaryComparator : binary:值
BinaryPrefixComparator : binaryprefix:值
NullComparator : null
SubstringComparator: substring:值
如果不知道过滤器的构造参数, 可以查看此地址:
http://hbase.apache.org/2.2/devapidocs/index.html
需求: 查询rowkey中以rk开头的数据
scan 'test01',{FILTER=>"PrefixFilter('rk')" }
需求: 查询在列名中包含a字母的列有那些呢?
scan 'test01',{FILTER=>"QualifierFilter(=,'substring:a')" }
需求: 查询 name的值包含z的数据, 带有Exclude 表示是否去除查询列
scan 'test01',{FILTER=>"SingleColumnValueFilter('f1','name',=,'substring:z')" }
scan 'test01',{FILTER=>"SingleColumnValueExcludeFilter('f1','name',=,'substring:z')" }
二、HBase的Java Api的操作
1.导入相关的pom文件
<repositories><!--代码库-->
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>
2.代码实现增删改查操作
package com.itheima.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class HBasePra {
private Connection hbaseConn;
private Admin admin;
private Table table;
//初始化链接对象
@Before
public void init() throws Exception {
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
hbaseConn = ConnectionFactory.createConnection(conf);
admin = hbaseConn.getAdmin();
table = hbaseConn.getTable(TableName.valueOf("WATER_BILL"));
}
// 如何创建一张表
@Test
public void createTableTest() throws Exception{
boolean flag = admin.tableExists(TableName.valueOf("WATER_BILL"));
if (!flag){
ColumnFamilyDescriptor familyDescriptor =
ColumnFamilyDescriptorBuilder.newBuilder("C1".getBytes()).build();
TableDescriptor tableDescriptor = TableDescriptorBuilder
.newBuilder(TableName.valueOf("WATER_BILL"))
.setColumnFamily(familyDescriptor).build();
admin.createTable(tableDescriptor);
}
}
// 如何添加一条数据
@Test
public void addDataTest() throws Exception {
Put put = new Put("4944191".getBytes());
put.addColumn("C1".getBytes(),"NAME".getBytes(),"登为红".getBytes());
put.addColumn("C1".getBytes(),"ADDRESS".getBytes(),"贵州省".getBytes());
put.addColumn("C1".getBytes(),"SEX".getBytes(),"男".getBytes());
table.put(put);
}
@Test
// 根据rowkey如何查询一条数据
public void getDataTest() throws Exception {
Get get = new Get("4944191".getBytes());
Result result = table.get(get);
List<Cell> cellList = result.listCells();
for (Cell cell : cellList) {
byte[] cloneRow = CellUtil.cloneRow(cell);
String row = new String(cloneRow);
byte[] cloneFamily = CellUtil.cloneFamily(cell);
String family = new String(cloneFamily);
byte[] cloneQualifier = CellUtil.cloneQualifier(cell);
String qualifier = new String(cloneQualifier);
byte[] cloneValue = CellUtil.cloneValue(cell);
String value = new String(cloneValue);
System.out.println("rowkey: " + row + "columnFamily: " + family + "columnQualifier: " + qualifier + "value: " + value);
}
}
// 如何删除数据
@Test
public void deleteDataTest() throws Exception {
Delete delete = new Delete("4944191".getBytes());
delete.addColumn("C1".getBytes(), "SEX".getBytes());
table.delete(delete);
}
// 如何删除表
@Test
public void dropTableTest() throws Exception {
if (admin.tableExists(TableName.valueOf("WATER_BILL")) &&
admin.isTableEnabled(TableName.valueOf("WATER_BILL"))
){
admin.disableTable(TableName.valueOf("WATER_BILL"));
admin.deleteTable(TableName.valueOf("WATER_BILL"));
}
}
// 如何扫描表
@Test
public void scanTest() throws Exception {
Scan scan = new Scan();
scan.setLimit(10);
// 设置过滤器
// 方法1:
SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
"C1".getBytes(),
"RECORD_DATE".getBytes(),
CompareOperator.GREATER_OR_EQUAL,
new BinaryComparator("2020-06-01".getBytes()));
SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
"C1".getBytes(),
"RECORD_DATE".getBytes(),
CompareOperator.LESS_OR_EQUAL,
new BinaryComparator("2020-06-30".getBytes()));
FilterList filterList = new FilterList();
filterList.addFilter(filter1);
filterList.addFilter(filter2);
scan.setFilter(filterList);
//方法2
// SingleColumnValueFilter filter3 = new SingleColumnValueFilter(
// "C1".getBytes(),
// "RECORD_DATE".getBytes(),
// CompareOperator.EQUAL,
// new BinaryPrefixComparator("2020-06".getBytes())
// );
//
// scan.setFilter(filter3);
ResultScanner results = table.getScanner(scan);
// 遍历得到每一行的数据
for (Result result : results) {
List<Cell> cellList = result.listCells();
// 遍历得到每一个单元格的数据
for (Cell cell : cellList) {
byte[] cloneQualifier = CellUtil.cloneQualifier(cell);
String qualifier = Bytes.toString(cloneQualifier);
if ("NAME".equalsIgnoreCase(qualifier) ||
"NUM_USAGE".equalsIgnoreCase(qualifier) ||
"RECORD_DATE".equalsIgnoreCase(qualifier)
)
{
byte[] cloneRow = CellUtil.cloneRow(cell);
String row = Bytes.toString(cloneRow);
byte[] cloneFamily = CellUtil.cloneFamily(cell);
String family = Bytes.toString(cloneFamily);
byte[] cloneValue = CellUtil.cloneValue(cell);
Object value;
if ("NUM_USAGE".equalsIgnoreCase(qualifier)){
value = Bytes.toDouble(cloneValue);
}
else {
value = Bytes.toString(cloneValue);
}
System.out.println("rowkey: " + row + " columnFamily: "
+ family + " columnQualifier: " + qualifier +" value: " + value);
}
}
System.out.println("------------------------------------------------");
}
}
//关闭操作
@After
public void close() throws Exception {
admin.close();
table.close();
hbaseConn.close();
}
}
三、HBase的集群架构
四、HBase的读写流程
1.HBase的读取数据的流程
1) 客户端发起读取数据的请求, 首先会先连接zookeeper
2) 从zookeeper中获取一张 hbase:meta 表目前被哪个RegionSerer所管理
说明: hbase:meta 是HBase专门用于存储元数据的表, 此表只会有一个Region,也就是说这个Region只能被一个RegionServer所管理
3) 连接Meta表对应的RegionServer, 从这个表中获取, 要读取的表有哪些Region, 以及这些Region对应被哪些RegionServer所管理,从而确认要操作的RegionServer
注意:
如果执行Scan, 返回这个表所有Region对应的RegionServer的地址
如果执行get, 返回查询rowkey对应Region所在RegionServer的地址
4- 连接对应的RegionServer, 从RegionServer中对应Region中读取数据即可
读取顺序: 先内存 ---> blockCache(块缓存) ----> storeFile ---> 大HFile
注意: 读取块缓存只有在get操作才有效, 如果scan扫描 基本无意义
2.HBase的写入数据的流程
1- 由客户端发起写入数据的请求, 首先先连接zookeeper
2- 从zookeeper中获取hbase:meta表被那个RegionServer所管理
3- 连接对应RegionServer, 从Meta表获取要写入数据的表有哪些Region, 以及每个Region被那个RegionServer所管理, 然后根据Region的起始Rowkey 和 结束rowkey, 获取要写入数据的Region对应RegionServer的地址
大白话: 查询写入到那个Region, 这个Region被那个RegionServer所管理
4- 连接对应RegionServer, 开始进行数据写入操作, 写入时, 先将数据写入到这个RegionServer的HLog, 然后将数据写入到对应的Store模块下的MemStore中(可能写入多个MemStore), 当这两个位置都写入成功的时候, 客户端认为数据写入完成了
---------------以上 为客户端写入流程------------------------
异步操作: 上面客户端可能执行了多次后, 后续才会干活
-
随着客户端不断的写入操作, MemStore中数据会越来越多, 当MemStore的数据达到一定的阈值(128M/1h)后, 就会启动flush刷新线程, 将内存中数据"最终"刷新到HDFS上, 形成一个StoreFile文件
-
随着不断的进行Flush的刷新, 在HDFS上StoreFile文件会越来越多, 当StoreFlie文件达到一定的阈值(3个及以上)后,就会启动compact合并压缩机制, 将多个StoreFlie"最终"合并为一个大的HFile
-
随着不断的合并, HFile文件会越来越大, 当这个大的HFile文件达到一定的阈值("最终"10GB)后,就会触发Split机制, 将大的HFile进行一分为二的操作, 形成两个新的大HFile文件, 此时Region也会进行一分为二,形成两个新的Region, 一个Region管理一个新的大Hfile, 旧的大HFile和对应Region就会被下线
总结
今天和大家分享了一下HBase基础的API和一些概念的内容,下次会接着分享HBase的核心工作机制。