HBase数据模型
概述
HBase 的设计理念依据 Google 的 BigTable 论文,论文中对于数据模型的首句介绍。
Bigtable 是一个稀疏的、分布式的、持久的多维排序map。
之后对于映射的解释如下:“
该映射由行键、列键和时间戳索引:映射中的每个值都是一个未解释的字节数组。
最终HBase 关于数据模型和 BigTable 的对应关系如下:“
HBase 使用与 Bigtable 非常相似的数据模型。用户将数据行存储在带标签的表中。数
据行具有可排序的键和任意数量的列。该表存储稀疏,因此如果用户喜欢,同一表中的行可
以具有疯狂变化的列。“
最终理解 HBase 数据模型的关键在于稀疏、分布式、多维、排序的映射。其中映射 map
指代非关系型数据库的 kev-Value 结构。
HBase分布式环境部署
关闭:stop-hbase.sh
务必先关闭hbase 在关闭zookeeper
HBase的设计
物理架构
Master
主要进程,具体实现类为HMaster,通常部署在namenode上
功能:负责通过ZK监控RecionServer进程状态,同时是所有元数据变化
的接口。内部启动监控执行region的故障转移和拆分的线程。
RegionServer
主要进程,具体实现类为HRegionServer,部署在datanode上
功能:主要负责数据cell的处理。同时在执行区域的拆分和合并的时候 由RegionServer来实际执行
(如配置高可用则存在backup)
Hdfs集群存储表格数据HBase把表格的数据存储到hdfs上面,按照namespace- >table->region->store的格式划分文件夹存储。在store中存储HFile,内部为对应的cell。
Master
- 实现类为HMaster,负责监控集群中所有的 RegionServer 实例。主要作用如下: 管理元数据表格 hbase:meta,接收用户对表格创建修改删除的命令并执行
- 监控 region 是否需要进行负载均衡,故障转移和 region 的拆分。 通过启动多个后台线程监控实现上述功能: DLoadBalancer 负载均衡器 周期性监控 region 分布在 regionServer 上面是否均衡,由参数 hbase.balancer.period 控制周期时间,默认5分钟。CatalogJanitor 元数据管理器、 定期检查和清理hbase:meta中的数据。
CatalogJanitor元数据管理器的主要任务是确保这些元数据的准确性和一致性。它会定期检查.META.表中的元数据,查找并清理可能存在的过期、无效或冗余的数据。这种清理过程对于保持HBase集群的健康运行至关重要,可以避免由于元数据不一致或错误导致的各种问题。
具体来说,CatalogJanitor可能会清理的数据包括:
过期或孤立的元数据:当HBase中的表被删除或修改时,相关的元数据可能不再需要。CatalogJanitor会识别并删除这些不再需要的元数据。
冗余的元数据:有时,由于各种原因(如系统错误、重复操作等),.META.表中可能会出现重复的元数据。CatalogJanitor会检测和删除这些冗余数据。
不一致的元数据:如果HBase集群中的元数据出现不一致的情况(例如,某个表的元数据在两个不同的Region中都有记录),CatalogJanitor会尝试修复或删除这些不一致的数据。
MasterProcWAL master 预写日志处理器 把master 需要执行的任务记录到预写日志 WAL中,如果 master 宕机,让 backupMaster 读取日志继续干。
而meta就是专门用来存储和Region相关的信息。
Meat表内具体存放哪些信息:
rowkey:由四个字段拼接起来,分别是 表名-StratRow-TimeStamp-EncodedName。
数据分为4列:
- info:regioninfo:EncodedName、RegionName、Region的StartRow、Region的StopRow;
- info:seqnumDuringOpen:存储Region打开时的sequenceId;
- info:server:存储Region落在哪个RegionServer上;
- info:serverstartcode:存储所在的RegionServer启动时间戳;
Region Server 实现类为 HRegionServer,主要作用如下:
- 负责数据 cell 的处理,例如写入数据 put,查询数据get 等
- 拆分并region的实际执行者,有 master 监控,有 regionServer 执行。
- Zookeeper HBase 通过 Zookeeper 来做 master 的高可用、记录 RegionServer 的部署信息、并且存储 有 meta 表的位置信息。“ HBase 对于数据的读写操作时直接访问 Zokeeper 的,在2.3 版本推出Master Registry 模式,客户端可以直接访问 master。使用此功能,会加大对 master 的压力,减轻对 Zookeeper的压力。
4) HDFS HDFS为Hbase 提供最终的底层数据存储服务,同时为HBase 提供高容错的支持。
Region和Table
Region位于不同节点,而按照列族切分为store用于底层存储到不同的文件夹中,便于文件对应。
也就是说横着切 是为了存放不同节点,而竖着切是为了存放在同一节点的不同数据,比较列族可以无限扩张。
逻辑架构 - Row
HBase 表中的每行数据都由一个 RowKey 和多个 Column (列)组成,数据是按照RowKey的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以RowKey 的设计十分重要。
Hbase所有数据以字节方式存储,每行由以下五列组成
- Rowkey(行键) 行号
- 列号 列族+列名
- 时间戳(标记版本)以便更新 使用唯一时间戳维护多个Row版本
- type put写入 delete 删除
- value 数据值
HBase元数据管理
HBase应用场景示例
经过以上设计架构了解,可以理解以下场景hbase 的应用。
HBase的交互模式shell
[root@centos143 ~]# hbase shell
hbase(main):001:0> version -- 查看当前hbase版本
hbase(main):002:0> list_namespace -- 查看所有表空间
hbase(main):003:0> create_namespace 'xxy' -- 创建表空间
-- 创建表
hbase(main):005:0> create 'xxy:student','baseinfo','schoolinfo'
--查看表详情
hbase(main):008:0> describe 'xxy:student'
启用/禁用
hbase(main):007:0> disable 'xxy:student'
hbase(main):011:0> enable 'xxy:student'
hbase(main):009:0> is_disabled 'xxy:student'
hbase(main):010:0> is_enabled 'xxy:student'
--删除表,必须将要删除的表先禁用
hbase(main):014:0> disable 'xxy:student'
hbase(main):014:0> drop 'xxy:student'
-- 删除表空间前必须将表空间中的所有表全部删除
hbase(main):020:0> drop_namespace 'xxy'
-- 显示指定表空间内所有的表 show tables;
hbase(main):024:0> list_namespace_tables 'xxy'
查看表是否存在
hbase(main):026:0> exists 'xxy:student'
--添加一个列簇
hbase(main):031:0> alter 'xxy:student','teacherinfo'
--删除列簇 teacherinfo
hbase(main):031:0> alter 'xxy:student',{NAME=>'teacherinfo',METHOD=>'delete'}
--改变列簇版本限制
hbase(main):034:0> alter 'xxy:student',{NAME=>'baseinfo',VERSIONS=>3}
--插入数据
hbase(main):036:0> put 'xxy:student','rowkey1','baseinfo:name','tom'
Took 0.0591 seconds
hbase(main):037:0> put 'xxy:student','rowkey1','baseinfo:birthday','1990-01-09'
Took 0.0046 seconds
hbase(main):038:0> put 'xxy:student','rowkey1','baseinfo:age','34'
Took 0.0081 seconds
hbase(main):039:0> put 'xxy:student','rowkey1','schoolinfo:name','njyd'
Took 0.0059 seconds
hbase(main):040:0> put 'xxy:student','rowkey1','schoolinfo:address','nanjing'
-- get查询
hbase(main):041:0> get 'xxy:student','rowkey1'
查询指定表的指定列簇中的数据
hbase(main):042:0> get 'xxy:student','rowkey1','baseinfo'
hbase(main):043:0> get 'xxy:student','rowkey1','schoolinfo'
查询指定表中的指定列簇中的列
hbase(main):044:0> get 'xxy:student','rowkey1','schoolinfo:address'
hbase(main):045:0> put 'xxy:student','rowkey1','baseinfo:name','xxy'
hbase(main):047:0> put 'xxy:student','rowkey1','baseinfo:name','lijia'
查看历史版本数据,默认只保留当前版本
hbase(main):050:0> get 'xxy:student','rowkey1',{COLUMN=>'baseinfo:name',VERSIONS=>3}
Java Api
// Get client admin
Configuration config = HBaseConfiguration.create();
config.Path("/opt/install/hbase/conf/hbase-site.xml");
config.Path("/opt/install/hadoop/etc/hadoop/conf/core-site.xml");
connect= ConnectionFactory.createConnection(config);
admin = connect.getAdmin();
// 执行表操作
admin.createTable()
admin.disableTable()
admin.deleteTable()
admin.listTable()
kafka 写入 hbase
附上一个java 将kafka清洗完的生产者数据写入 hbase events_db:users表的实例
建表语句已在代码中注释
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.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
public class UsersTohb { //38209
static long rowNum=0;
public static void main(String[] args) {
Properties prop = new Properties();
prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.78.143:9092");
prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// 设置是否自动提交, false 手动提交, true 自动提交
prop.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
prop.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "500");
prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
// 配置消费者组
prop.put(ConsumerConfig.GROUP_ID_CONFIG, "users_group1");
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(prop);
consumer.subscribe(Collections.singleton("users")); //取
//hbase
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "192.168.78.143");
conf.set("hbase.zookeeper.property.clientPort", "2181");
conf.set("hbase.roodir", "hdfs://192.168.78.143:9000/hbase");
Connection conn = null;
try {
conn = ConnectionFactory.createConnection(conf);
// Table table = conn.getTable(TableName.valueOf("bigdata:teacher"));
TableName tableName = TableName.valueOf("events_db:users");
final BufferedMutator.ExceptionListener listener = new BufferedMutator.ExceptionListener() {
public void onException(RetriesExhaustedWithDetailsException exception, BufferedMutator mutator) throws RetriesExhaustedWithDetailsException {//监听失败
int nums = exception.getNumExceptions();
for (int i = 0; i < nums; i++) {
System.err.println("失败 保存" + exception.getRow(i) + ".");
}
}
};
BufferedMutatorParams parms = new BufferedMutatorParams(tableName);
parms.writeBufferSize(1024 * 1024);
parms.listener(listener);
parms.setWriteBufferPeriodicFlushTimeoutMs(2 * 1000);//2s 刷一次
BufferedMutator mutator = conn.getBufferedMutator(parms);
ArrayList<Put> list = new ArrayList<>();
while (true) {
list.clear();
ConsumerRecords<String, String> poll =
consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : poll) {
rowNum++;
String[] user = record.value().split(",");
Put put = new Put(user[0].getBytes());
// create 'events_db:users', 'profile', 'region', 'registration'
// create 'events_db:user_friend', 'uf'
// create 'events_db:events', 'schedule', 'location', 'creator', 'remark'
// create 'events_db:event_attendee', 'euat'
// create 'events_db:train', 'eu'
put.addColumn("region".getBytes(),"locale".getBytes(),user[1].getBytes());
put.addColumn("profile".getBytes(),"birthday".getBytes(),user[2].getBytes());
put.addColumn("profile".getBytes(),"gender".getBytes(),user[3].getBytes());
if(user.length>4){
put.addColumn("registration".getBytes(),"joinAt".getBytes(),user[4].getBytes());
}
if(user.length>5){
put.addColumn("region".getBytes(),"location".getBytes(),user[5].getBytes());
}
if(user.length>6){
put.addColumn("region".getBytes(),"timezone".getBytes(),user[6].getBytes());
}
list.add(put);
}
if(list.size()>0)
mutator.mutate(list);
}
//Thread.sleep(1000);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
scan表中数据如下
为什么说hbase很脆呢,因为笔者在学习过程中真的玩坏了很多次,请每次学习结束后务必关闭hbase后再挂起(先关闭啥再关啥想清楚),其他东西不一定坏,hbase是真的脆
尝试用线程池 向hbase 写入模拟的几百万条数据 写崩过好几次,后来乖乖单线程 慢慢等。
坏了之后修需 删掉hbase 跟 zookeeper cli底下的相关文件,当然表数据自然全无。
还请细心照料!!!、