HBase-Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。当你需要对庞大数据量(上至千万亿字节)进行随机实时读写操作时,HBase是一个很好的选择。不同于传统的关系型数据库,HBase是一种非关系型数据库,即NoSQL数据库。
Google三驾马车:GFS、MapReduce和BigTable代表Google针对大数据存储与处理提出的三种框架。其中,GFS是面向大规模数据密集型应用的、可伸缩的分布式文件系统(HFDS);MapReduce是处理海量数据的分布式并行计算的执行框架(MapReduce);BigTable是处理海量数据的分布式结构化数据存储系统。HBase的体系结构是一个主从架构,主节点HMaster在整个集群中只有一个Active,从节点HRegion Server有多个运行,整个集群通过Zookeeper(单数)来维护集群中的Server状态,Zookeeper存储顶级索引入口。
底层实现:布隆过滤器+跳表(多层索引表,每个索引范围数据存储不同节点),Java语言编写
表(Table):HBase将数据组织成表;
行(Row):在表内,数据存储在其对应的行中。每一行都有一个唯一的行键(Row Key)进行标识。
列族(Column Family):在行内,数据被分组为不同的列族。同一张表中,每一行列族的划分都是相同的。同时列族必须作为表模板预先定义好。
列限定符(Column Qualifier):或称为列(Column),以列族作为前缀,每个列族可有有多个列成员中,如列族名:列限定符名。与列族不同的是,每行的列限定符都可以不相同,也不需要在建表时定义。
单元格(Cell):一组行键、列族和列限定符的组合标明了一个唯一的单元格。存储在单元格中的数据被称作单元格的值(Value)。
时间戳(Timestamp):每个单元格的值都是版本化的,有一至多个版本(Version),每个版本由其版本号标明。默认情况下,版本号是其对应值写入单元格时给定的时间戳,若写入值时未给定时间戳,则时间戳为写入时的当前时间;若读取值时未给定时间戳,则返回最新的版本。
强一致性:当Write放回时,所有的reader都会读到同样的值;
自动扩展性:数据变大时region会分裂,使用HDFS存储备份数据;
内置恢复功能:使用WAL实现故障恢复;
Hadoop结合:底层使用MapReduce处理HBase数据。
数据模型展现了两行存储在HBase表中的两行数据,此表有两个列族,行与列的交叉点为单元格,每个单元格中的数据又分为多个版本。要注意的是,表中的每行都要有相同的列族,却可以有不同的列限定符
HBase表的第一行数据呈现为了多维键值对。当检索行键“00001”下的列族“HikDepartment”时,将得到该列族下的“Primary”和“Secondary”两组键值对。
HBase是由三种类型的server组成的主从架构:
Region Server:负责管理Region处理数据的读写请求,客户端请求数据时直接和Region Server交互。
HBase Master:负责Region的分配,DDL(创建,删除)等操作。
Zookeeper:作为HDFS的一部分,负责维护集群状态。
注:Hadoop DataNode负责存储Region Server所管理的数据,HBase所有数据都存储在HDFS文件中。Region Server与HDFS DataNode往往向数据靠拢,即数据本地化。
Regions:是HBase的最小数据单元,table根据rowkey的范围被水平拆分为若干个Region,每个Region包含了start key和end key之间的所有行,所有Region统一由Region Server管理,进行数据的读写。
HBase Master:即HMaster,1)监控集群中和所有的Region Server,在集群启动时分配Region,故障恢复和负载均衡时重新分配Region;2)向Client提供创建、删除和更新HBase Table的接口。
Zookeeper:维护HBase集群中所有服务的状态,维护集群的健康情况和故障通知,同时使用一致性协议(ZAB)保证分布式状态一致性(单数台机器)。Zookeeper与active HMaster、Region Server保持会话(Session),并通过心跳检测维护所有临时节点。HMaster们会竞争创建临时节点,Zookeeper决定谁作为active HMaster,其他作为Standby,然后active HMaster监控每个Region Server(每个创建一个临时节点)。
HBase Meta Table:是一个特殊的HBase table,保存了系统中所有region列表,类似一个b-tree,Zookeeper保存了meta table的位置。其结构为:
Key:table,region start key,region id
Value:region server
Region Server由四个部分构成,具体分为如下:
WAL:Write Ahead Log,用于存储新的还未被持久化存储的数据,被用来做故障恢复。
BlockCache:读缓存,在内存中存储了最常访问的数据,采用LRU(Least Recently Used)缓存。
MemStore:写缓存,在内存中存储了还未被持久化到硬盘的数据,当数据写入磁盘时,数据会首先被排序,每个Region的每个Column Family都会有一个MemStore,所有更新都以Column Family为单位进行排序。
HFile:在磁盘上(HDFS)存储的数据,以有序键值对形式,当MemStore累积了足够多的数据后,整个有序数据集就会被写入一个新的Hfile文件,整个过程是一个顺序写操作,不需要移动磁盘头,因此速度非常快。
HFile使用多次索引来查询数据而不必读取整个文件,这种多层索引类似于一个B+ tree:
其中,HFile被打开时会被载入内存,这样数据查询只要一次硬盘查询。
1) 第一次读写操作:
2) HBase写数据流程:
当Client发起一个写操作请求,第一步首先将数据写入到WAL中,新数据会被追加到WAL文件尾部,数据被写入WAL后,会被加入到写缓存MemStore中,然后服务端向Client返回ACK应答。
HBase Region Flush
当MemStore中累积了足够多的数据后,整个有序数据集就会被写入一个新的HFile文件到HDFS上,然后记录最后写入数据的最大序列号(sequence number)。HBase同样为每个Column Family都创建一个HFile,里面存储了具体的Cell。
3) HBase读数据流程:
因为缓存,写入的时间不同,读取某行(rowkey)的cell时,数据可能位于不同地方。因此一次读操作系统会将BlockCache,MemStore和HFile中的Cell进行合并:
注:每个MemStore可能会有多个HFile,所以一次读操作可能需要读多个文件,则可能影响性能,这被称为读放大(Read Amplification)
HBase Minor Compaction
HBase会自动合并一些小的HFile,重写成少量更大的HFile,该过程被称为Minor Compaction,使用归并排序算法,将小文件合成大文件,有效减少HFile数量。
HBase Major Compaction
Major Compaction合并重写每个Column Family下的所有HFile,成为一个单独的HFile,过程中,被删除和过期的数据真正意义上被删除,该过程会重写所有HFile,提高读性能,但是会产生大量IO和网络开销,因此灰暗在半夜和周末执行,这种被称为写放大(Write Amplification)
Region分裂
一开始每个table只有一个region,当一个region逐渐变得很大时,它会分裂(split)成两个字region,每个region都包含原来一半的数据,这两个region并行在原Region Server上创建,分裂动作会报告给HMaster,(处于负载均衡目的,HMaster可能会将新的region迁移至其他Region Server)。
Read负载均衡
Splitting出于负载均衡会将新的Region迁移至其他Region Server,这会导致那些Region Server需要访问离它比较远的HDFS数据,直到major compaction到来,会将那些远方的数据重新迁移到region server节点附件的地方。
HDFS数据备份
所有读写都发生在HDFS的主DataNode节点上,HDFS会自动备份WAL和HFile的文件blocks,HBase依赖HDFS保证数据的完整安全,当数据写入HDFS时,一份写入本地节点,另外两个备份会被写入其他节点。
HBase故障恢复
当HMaster发现某个region server故障,HMaster会将这个region server锁管理的regions分配给其他健康region server,为了恢复故障region server中MemStore还未被持久化到HFile的数据,HMaster会将WAL分割为几个文件,将他们保存到不同新的region server,每个region server回放自己拿到的WAL碎片中的数据,为它们分配到新的Region建立Memstore。
1、从Zookeeper拿到meta表信息
2、根据rowkey确定往哪个RegionServer写
3、写MemStore内存同时记录日志
4、内存满后刷写StoreFile
5、StoreFile一定条件下触发合并
1、从Zookeeper拿到meta信息
2、找到数据分布的RegionServer
3、分别在MemStore和StoreFile查找
4、根据数据版本组织key-value进行返回
根据使用场景的不同,HBase提供了两种方式操作集群:(1)在Linux Shell环境下利用HBase自带的HBase shell脚本;(2)支持Rest风格的Http API访问HBase(3)HBase对多种编程语言提供的API调用,演示以Java API为例。
启动
./bin/start-hbase.sh
/单例HMaster进程
./bin/hbase-daemon.sh start master
单例HRegionServer进程
./bin/hbase-daemon.sh start regionserver
停止
./bin/stop-hbase.sh
单例HMaster进程
./bin/hbase-daemon.sh stop master
单例HRegionServer进程
./bin/hbase-daemon.sh stop regionserver
--Hbase shell
--status 状态查看
--version 版本查看
list—— 列出HBase中存在的所有表
create ‘表名’ , ‘列族1’ , ‘列族2’ , ‘列族N’—— 创建表
desc/describe ‘表名’—— 查看表的基本信息
enable/disable ‘表名’—— 启用/禁用表
is_ enable/ is_ disable ‘表名’—— 查看该表是否启用/禁用
drop ‘表名’—— 删除表 注:删除前先禁用disable ‘表名’
alter ‘表名’ , ‘列族名’—— 添加列族
alter ‘表名’ , {NAME => ‘列族名’ , METHOD=>‘delete’}—— 删除列族
alter ‘表名’ , {NAME => ‘列族名’ , VERSION=>3}—— 更改列族存储版本
put ‘表名’ , ‘行键’ , ‘列族:列’ , ‘值’—— 插入数据
delete ‘表名’ , ‘行键’ , [ ‘列族:列’]—— 删除指定行、列的信息
HBase中访问数据有两种基本的方式:
get ‘表名’ , ‘行键’ , [‘列族’ / ‘列族:列’]—— 获取指定行、列族、列的信息
scan ‘表名’ —— 查询整表数据
scan ‘表名’ , {COLUMN=>[‘列族’ / ‘列族:列’]}—— 查询列族/列的数据
scan ‘表名’ , FILTER=>”ValueFilter (=,’binary:xx / substring:yy’) / CoulmnPrefixFilter ‘zz’) [AND / OR] ”—— 条件查询值等XX / 值包含YY /列名前缀为ZZ / [多条件查询]的数据
scan ‘表名’ , FILTER=>”PrefixFilter(‘xx’)”—— 查询Rowkey前缀判断
//创建配置文件
Configuration conf = new Configuration();
conf.set("hbase.zookeeper.quorum","10.3.69.118");
conf.set("hbase.zookeeper.property.clientPort","31704");
conf.set("zookeeper.znode.parent","/hbp_root/kobe/hbase");
注:Hbase是基于Zookeeper维护的服务,配置Hbase的服务IP、端口Port以及操作文件路径。
connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
HBase2.0.0之前:
TableName tableName = TableName.valueOf("table_name");
HTableDescriptor htable = new HTableDescriptor(tableName);
HColumnDescriptor family = new HColumnDescriptor("family_name");
htable.addFamily(family);
admin.createTable(htable);
HBase2.0.0之后弃用HTableDescriptor和HColumnDescriptor,并在HBase3.0.0之后彻底删除,使用TableDescriptorBuilder和ColumnFamilyDescriptor替代。
HBase2.0.0之后:
//创建表名为HBase_test的表
TableName tableName = TableName.valueOf("HBase_test");
TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tableName);
//创建列族1
ColumnFamilyDescriptor family1 = ColumnFamilyDescriptorBuilder.of("column_1");
tableDescriptorBuilder.setColumnFamily(family1);
//创建列族2
ColumnFamilyDescriptor family2 = ColumnFamilyDescriptorBuilder.of("column_2");
tableDescriptorBuilder.setColumnFamily(family2);
TableDescriptor build = tableDescriptorBuilder.build();
admin.createTable(build);
//向'hbasetest'数据表插入数据
TableName name = TableName.valueOf("hbasetest");
Table htable = connection.getTable(name);
String rowkey = "r1";
for(int i=0;i<3;i++){
//操作put对象
Put put = new Put(Bytes.toBytes(rowkey));
put.addColumn("cf".getBytes(),"name1".getBytes(),"this is name1".getBytes());
put.addColumn("cf".getBytes(),"name2".getBytes(),"this is name2".getBytes());
put.addColumn("cf".getBytes(),"age1".getBytes(),"this is age1".getBytes());
put.addColumn("cf".getBytes(),"age2".getBytes(),"this is age2".getBytes());
put.addColumn("cf".getBytes(),"time1".getBytes(),"this is time1".getBytes());
put.addColumn("cf".getBytes(),"time2".getBytes(),"this is time2".getBytes());
htable.put(put);
}
htable.close();
//遍历HBase中的所有数据表
for(TableName index:admin.listTableNames()){
System.out.println(index);
}
TableName name = TableName.valueOf("hbasetest");
Table htable = connection.getTable(name);
String rowkey = "r1";
//获取数据
Get get = new Get(Bytes.toBytes(rowkey));
Result result = htable.get(get);
for(Cell kv:result.rawCells()){
System.out.println(Bytes.toString(CellUtil.cloneValue(kv)));
}
htable.close();
参考:https://www.cnblogs.com/sujing/p/10960832.html