HBase 在日常中的使用
HBase是什么?
HBase是一个底层基于HDFS存储、开源的、面向列(Column-Oriented)、适合存储海量非结构化数据或半结构化数据的、具备高可靠性、高性能、可灵活扩展伸缩的、支持实时数据读写的分布式存储系统。
关于存储在HBase 的表特征有:
表:一个表通常可以有上亿行,上百万列;
面向列:面向列(族)的存储;
稀疏:表中为空(null)的列不占用存储空间。
HBase的结构
HBase集群由主备Master进程和多个RegionServer进程组成。如下图所示:

图1中有几个角色,其中包括:HMaster、RegionServer、Zookeeper、HDFS、Client。
HMaster:在HA高可用模式下,包含主用Master和备用Master。主用Master:负责HBase中RegionServer的管理,包括表的增删改查;RegionServer的负载均衡,Region分布调整;Region分裂以及分裂后的Region分配;RegionServer失效后的Region迁移等;备用Master:当主用Master故障时,备用Master将取代主用Master对外提供服务。故障恢复后,原主用Master降为备用。
RegionServer:负责提供表数据读写等服务,是HBase的数据处理和计算单元,负责维护一系列的region。RegionServer一般情况与HDFS集群的DataNode部署在一起,实现数据的存储功能。
Zookeeper:用来为HBase集群中各进程提供分布式协作服务。各个RegionServer将自己的信息注册到ZooKeeper中,主用Master据此感知各个RegionServer的健康状态等信息。
HDFS:作为HBase的底层数据存储底座,为HBase提供高可靠的文件存储服务,HBase的数据全部存储在HDFS中。
Client:使用HBase的RPC机制与Master、RegionServer进行通信。Client与Master进行管理类通信,与RegionServer进行数据操作类通信。
HBase原理
HBase的数据模型
HBase以表的形式存储数据,数据模型如图所示。表中的数据划分为多个Region,并由Master分配给对应的RegionServer进行管理。每个Region包含了表中一段RowKey区间范围内的数据,HBase的一张数据表开始只包含一个Region,随着表中数据的增多,当一个Region的大小达到容量上限后会分裂成两个Region。您可以在创建表时定义Region的RowKey区间,或者在配置文件中定义Region的大小。

HBase数据模型主要包含以下几种:Name Space 、Region、Row、Column Family、Column、Time Stamp、Cell。
Name Space :命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase 有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表, default 表是用户默认使用的命名空间。
Region:类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需 要声明具体的列。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关 系型数据库相比,HBase 能够轻松应对字段变更的场景。
Row:HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重 要。
Column Family:列族,一个表在水平方向上由一个或多个Column Family组成。一个CF(Column Family)可以由任意多个Column组成。Column是CF下的一个标签,可以在写入数据时任意添加,因此CF支持动态扩展,无需预先定义Column的数量和类型。HBase中表的列非常稀疏,不同行的列的个数和类型都可以不同。此外,每个CF都有独立的生存周期(TTL)。可以只对行上锁,对行的操作始终是原始的。
Column:列,与传统的数据库类似,HBase的表中也有列的概念,列用于表示相同类型的数据。
Time Stamp:每次数据操作对应的时间戳,数据按时间戳区分版本,每个Cell的多个版本的数据按时间倒序存储。
Cell:HBase最小的存储单元,由Key和Value组成。Key由row、column family、column qualifier、timestamp、type、MVCC version这6个字段组成。Value就是对应存储的二进制数据对象。
RegionServer数据存储

HBase数据存储包括:Store、MemStore、StoreFile、HFile、HLog。
Store:一个Region由一个或多个Store组成,每个Store对应模型中的一个Column Family。
MemStore:一个Store包含一个MemStore,MemStore缓存客户端向Region插入的数据,当RegionServer中的MemStore大小达到配置的容量上限时,RegionServer会将MemStore中的数据“flush”到HDFS中。
StoreFile:MemStore的数据flush到HDFS后成为StoreFile,随着数据的插入,一个Store会产生多个StoreFile,当StoreFile的个数达到配置的最大值时,RegionServer会将多个StoreFile合并为一个大的StoreFile。
HFile:HFile定义了StoreFile在文件系统中的存储格式,它是当前HBase系统中StoreFile的具体实现。
HLog:HLog日志保证了当RegionServer故障的情况下用户写入的数据不丢失,RegionServer的多个Region共享一个相同的HLog。
数据读写流程、flush时机、compact、Region split
1. 数据写流程

写流程:
1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,
查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以
及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)将数据顺序写入(追加)到 WAL;
5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
6)向客户端发送 ack;
7)等达到 MemStore 的刷写时机后,将数据刷写到 HFile。
2. MemSore File刷写时机

如图所示:
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.当 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。自动刷新的时间间隔由该属性进行
配置 hbase.regionserver.optionalcacheflushinterval(默认 1 小时)。
3. 数据读流程

1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,
查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以
及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将
查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不
同的类型(Put/Delete)。
5) 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到
Block Cache。
6)将合并后的最终结果返回给客户端。
4.compact合并

由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)
和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。
为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。
Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。Minor Compaction
会将临近的若干个较小的 HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。
Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期
和删除的数据。
5. Region split
默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进
行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,
HMaster 有可能会将某个 Region 转移给其他的 Region Server。
Region Split 时机:
当 1 个 region 中 的 某 个 Store 下所有 StoreFile 的 总 大 小 超 过 Min(R^2 *
"hbase.hregion.memstore.flush.size",hbase.hregion.max.filesize"),该 Region 就会进行拆分,其 中 R 为当前 Region Server 中属于该 Table 的个数,如下图所示。

HBase 基础使用
常用的的基本命令行操作包括:put、get、scan,delete这四种为表级别增、删、改、查操作,其中:
put:新增,如果目标数据存在且时间戳大于等于插入时时间戳,也为修改操作;
get、scan:查询操作,一种为指定rowkey获取单条数据,一种为rowkey范围扫描获取一批数据;
delete :删除一条数据;
以下几种为表级别DDL:create、disable、enable、drop、truncate、desc、alter等,其中:
create:创建一张表,需注意的是,建表时无需指定字段类型,但必须指定列族名称;
disable:在删除表时,需使用该命令事先将表停用;
enable : 与disable相反,将表上线;
drop : 删除表;
truncate:将表清空,如果预先对表预分区,请谨慎使用该命令,该命令为删除后重建,事先预分区的信息会丢失;
desc : 查看表信息,如使用什么压缩算法、记录版本数量等;
alter : 修改表信息,如将原先表snappy压缩算法修改为gzip等。
具体相关命令细节,可登录hbase shell 中使用help ‘命令’查看。
HBase样例代码
1.创建Configuration
private static void init() throws IOException {
//创建配置
conf = HBaseConfiguration.create();
//windows操作系统
//String userdir = TestMain.class.getClassLoader().getResource("conf").getPath() + File.separator;[1]
//linux操作系统
//String userdir = System.getProperty("user.dir") + File.separator + "conf" + File.separator;
conf.addResource(new Path(userdir + "core-site.xml"), false);
conf.addResource(new Path(userdir + "hdfs-site.xml"), false);
conf.addResource(new Path(userdir + "hbase-site.xml"), false);
}
首先创建配置对象,通过类加载器将配置文件读取,并添加资源到配置对象中。
2.创建Connection
prublic Connection HBaseConn(Configuration conf){
TableName tableName = TableName.valueOf("hbase_table_name");
Connection conn = ConnectionFactory.createConnection(conf);
}
通过传入配置对象,获取HBase表名称,获取HBase的连接对象。
3.创建表
public void CreateTable(TableName tableName) {
//指定表描述符
TableDescriptorBuilder tdb = TableDescriptorBuilder.newBuilder(tableName);(1)
//设置列族信息
ColumnFamilyDescriptorBuilder cdb = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("列族名"));
// 设置HBase数据编码格式, HBase支持DIFF,FAST_DIFF,PREFIX三种
cdb.setDataBlockEncoding(DataBlockEncoding.FAST_DIFF);
//设置压缩格式,HBase支持gzip和snappy两种
cdb.setCompressionType(Compression.Algorithm.SNAPPY);
tdb.setColumnFamily(cdb.build());
Admin admin = null;
try {
admin = conn.getAdmin();
if (!admin.tableExists(tableName)) {
admin.createTable(tdb.build());
} else {
LOG.warn("表已经存在");
}
} catch (IOException e) {
LOG.error("创建表失败" ,e);
} finally {
if (admin != null) {
try {
//关闭admin资源
admin.close();
} catch (IOException e) {
LOG.error("关闭admin失败" ,e);
}
}
}
}
大致流程如下:
1.创建表描述符;2.创建列族描述符;3.添加列族描述符到表描述符中;4.获取Admin对象,Admin提供了建表、创建列族、检查表是否存在、修改表结构和列族结构以及删除表等功能;5.调用Admin的建表方法;6.关闭admin资源。
备注:
//设置编码算法,HBase提供了DIFF,FAST_DIFF,PREFIX三种编码算法 cdb.setDataBlockEncoding(DataBlockEncoding.FAST_DIFF); //设置文件压缩方式,HBase默认提供了GZ和SNAPPY两种压缩算法 //其中GZ的压缩率高,但压缩和解压性能低,适用于冷数据 //SNAPPY压缩率低,但压缩解压性能高,适用于热数据 //建议默认开启SNAPPY压缩 cdb.setCompressionType(Compression.Algorithm.SNAPPY);
4.删除表
public void dropTable(TableName tableName) {
Admin admin = null;
try {
admin = conn.getAdmin();
if (admin.tableExists(tableName)) {
//第一步现将表下线
admin.disableTable(tableName);
//第二步删除
admin.deleteTable(tableName);
}
LOG.info("删除成功");
} catch (IOException e) {
LOG.error("删除失败" ,e);
} finally {
if (admin != null) {
try {
//关闭admin资源
admin.close();
} catch (IOException e) {
LOG.error("关闭失败" ,e);
}
}
}
}
5.插入数据
public static void addRowData(String tableName, String rowKey, String columnFamily, String column, String value) throws IOException{
HTable hTable = new HTable(conf, tableName);
Put put = new Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(columnFamily), Bytes.toBytes(column),
Bytes.toBytes(value));
hTable.put(put);
hTable.close();
}
大致流程如下:
1.创建HTable对象;2.构建Put对象;3.向Put对象中封装数据;4.调用put方法插入数据;5.关闭资源。
6.删除数据
public static void deleteMultiRow(String tableName, String... rows) throws IOException{
HTable hTable = new HTable(conf, tableName);
List<Delete> deleteList = new ArrayList<Delete>();
for(String row : rows){
Delete delete = new Delete(Bytes.toBytes(row));
deleteList.add(delete);
}
hTable.delete(deleteList);
hTable.close();
}
大致流程如下:
1.创建HTable对象;2.构建Delete 集合对象;3.向Delete集合对象中封装Delete对象并封装数据;4.调用delete方法插入数据;5.关闭资源。
7.使用Get读取数据
public static void getRow(String tableName, String rowKey) throws IOException{
HTable table = new HTable(conf, tableName);
Get get = new Get(Bytes.toBytes(rowKey));
Result result = table.get(get);
for(Cell cell : result.rawCells()){
System.out.println(" 行 键 :" +Bytes.toString(result.getRow()));
System.out.println(" 列 族 " + Bytes.toString(CellUtil.cloneFamily(cell)));
System.out.println(" 列 :" + Bytes.toString(CellUtil.cloneQualifier(cell)));
System.out.println(" 值 :" + Bytes.toString(CellUtil.cloneValue(cell)));
System.out.println("时间戳:" + cell.getTimestamp());
}
table.close();
}
大致流程如下:
1.创建HTable对象;2.根据rowkey构建Get对象;3.获取get返回结果;4.遍历结果并打印;5.关闭资源。
8.使用Scan读取数据
public static void getAllRows(String tableName) throws IOException{
HTable hTable = new HTable(conf, tableName);
Scan scan = new Scan();
ResultScanner resultScanner = hTable.getScanner(scan);
for(Result result : resultScanner){
Cell[] cells = result.rawCells();
for(Cell cell : cells){
System.out.println(" 行 键 :" + Bytes.toString(CellUtil.cloneRow(cell)));
System.out.println(" 列 族 " + Bytes.toString(CellUtil.cloneFamily(cell)));
System.out.println(" 列 :" + Bytes.toString(CellUtil.cloneQualifier(cell)));
System.out.println(" 值 :" + Bytes.toString(CellUtil.cloneValue(cell)));
}
}
}
大致流程如下:
1.创建HTable对象;2.根据rowkey范围构建Scan对象;3.获取scan返回结果;4.遍历结果并打印;5.关闭资源。
9.HBase对接Hive
CREATE EXTERNAL TABLE hive_external_table_name(
rowkey string COMMENT '',
col1 string COMMENT '',
col2 string COMMENT '',
col3 string COMMENT '',
col4 string COMMENT '',
col5 string COMMENT '')
ROW FORMAT SERDE
'org.apache.hadoop.hive.hbase.HBaseSerDe'
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
'hbase.columns.mapping'=':key,cf1:col1,cf1:col2,cf1:col3,cf1:col4,cf1:col5',
'serialization.format'='1')
TBLPROPERTIES (
'hbase.mapred.output.outputtable'='HBase_namespace:HBase_table_name','hbase.table.name'='HBase_namespace:HBase_table_name');
HBase与Hive的映射是通过Hive的HBaseStorageHandler组件实现的,它充当了Hive和HBase之间的桥梁,使Hive能够将HBase表映射为Hive外部表,从而通过SQL语句操作HBase数据。(需要注意的是:现版本通过hive创建HBase映射表还无法获取HBase表中每个字段的时间戳,但可以获取最大的时间戳作为HQL条件查询。通过Hive映射表写入HBase时,时间戳会根据系统时间生成,如果HBase中timestamp值取值为业务时间,那么在后续数据重新入库时将收到影响,导致数据无法写入的风险)
HBase数据迁移
1.原始数据表进行快照 : snapshot 'hbase_src_tablename','hbase_src_tablename_snapshot'
2.将表快照进行迁移 :linux
hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
-Dsnapshot.export.skip.tmp=true \
-snapshot hbase_src_table_name_backup_snapshot \ --将要迁移的快照和快照那一时刻表的数据
-copy-from hdfs://源集群NN IP:port/hbase \ --从哪个hdfs集群(旧)
-copy-to hdfs://目的集群NN IP:port/hbase \ --到哪个hdfs集群(新)
-mappers 16 \ --mapper的个数
-bandwidth 20 --带宽
3.对迁移过来的表快照恢复:clone_snapshot 'hbase_src_tablename_snapshot','hbase_des_tablename'
4.major_compact 'hbase_des_tablename'
备注:
1.创建一个快照后,对应hdfs /hbase/.hbase-snapshot目录下会记录一个快照名的目录,里面有一个文件记录打快照的那张表下打快照那一刻起所有的storefile;
2.将快照恢复到另外一张新表后,在没有执行major_conpact命令之前,这张新表中storefile所存储的是一个引用,例如: 原始表=A(region名)-B(storefile名);如果基表不删除,在合并过后,可以通过从archive目录:原始表=A(region名)-B(storefile名)进行查看。
3.在原始表(也就是创建快照的基表)进行合并的时候,如果storefile还存在引用,那么不会直接删除,而是将数据保存到hdfs路径下的archive;
特别说明:使用HBase snapshot的方式做数据迁移时,一定要核对表是否完全合并完成(HBase表目录下所有StoreFile中没有不规则的命名方式“原始表=A(region名)-B(storefile名)”)。只有在完全合并完成后,才意味着所有链接文件的数据已经合并到表目录下,这时才可以将快照进行删除,不然会导致Region处于RIT状态,导致丢数据的风险。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)