您现在的位置是:首页 >技术杂谈 >HBase架构篇 - Hadoop家族的天之骄子HBase网站首页技术杂谈

HBase架构篇 - Hadoop家族的天之骄子HBase

等後那场雪 2023-05-17 12:00:04
简介HBase架构篇 - Hadoop家族的天之骄子HBase

HBase的基本组成结构

在这里插入图片描述

表(table)

HBase 的数据存储在表中。表名是一个字符串。表由行和列组成。

行(row)

HBase 的行由行键(rowkey)和 n 个列(column)组成。行键没有数据类型,可以看作是字节数组,类似于关系型数据库的主键索引,在整个 HBase 表中是唯一的,按照字母顺序排序。

列族(column family)

HBase 的列族由多个列组成,相当于将列进行分组。列的数量没有限制。表中的每一行都有同样的列族。列族必须在表创建的时候指定,不能轻易修改,并且数量不能太多,一般不超过 3 个。列族名的类型是字符串。

列限定符(qualifier)

列限定符用于代表 HBase 表中列的名称,列族中的数据通过列限定符来定位,常见的定位格式为 “family:qualifier”(比如定位列族 cf1 的列 name,则使用 cf1:name)。一个列族下面可以有多个列限定符。列限定符没有数据类型,可以看作是字节数组。

单元格(cell)

单元格通过行键、列族、列限定符一起来定位。单元格包括值和时间戳。值没有数据类型,总是视为字节数组。时间戳代表该值的版本,类型为 long。默认,时间戳表示数据写入服务器的时间,但是当数据放入单元格时,也可以指定不同的时间戳。每个单元格都根据时间戳保存着同一份数据的多个版本,并且按照降序排列,即最新的数据排在前面。对单元格中的数据进行访问的时候会默认读取最新值。

{
	"00001": {				                    // 行键
		"info": {			                    // 列族
			"username": {	                    // 列限定符												
				"15335401223674": "zhangsan"	// 时间戳:列值				 
			},																						 
			"password": {						   -----
				"1533540265719": "hello",				|
				"1533540102020": "123"					| --> 单元格
			}									    -----
		}
	}
}

HBase的架构设计

HMaster

HMaster 节点可以有多个。通过 ZooKeeper 的选举机制保证同一时刻只有一个 HMaster 节点处于活动状态,其它 HMaster 节点处于备用状态。

HMaster 节点的特点如下:

  • HMaster 节点本身不存储 HBase 的任何数据。它主要用于管理 HRegionServer 节点,指定 HRegionServer 节点可以管理哪些 HRegion,以实现其负载均衡。
  • 当某个 HRegionServer 节点宕机时,HMaster 会将其中的 HRegion 迁移到其它的 HRegionServer 上。
  • 管理用户对表的增删改查操作。
  • 管理表的元数据(每个 HRegion 都有一个唯一标识符,元数据主要保存这个唯一标识符与 HRegionServer 的映射关系)。
  • 权限管理。

HRegion、HRegionServer

HBase 通过 rowkey 自动将表水平切分成多个区域,这个区域称为 HRegion。每个 HRegion 由表中的多行数据组成。

最初一个表只有一个 HRegion,随着数据的增多,当数据大到一定的值后,便会在某行的边界上将表分割成两个大小基本相同的HRegion。然后由 HMaster 节点将不同的 HRegion 分配到不同的 HRegionServer 节点上,由 HRegionServer 节点对其进行管理以及响应客户端的读写请求。换言之,分布在集群中的所有 HRegion 按序排列就组成了一张完整的表。

每个 HRegion 记录了 rowkey 的起始行键(startkey)、结束行键(endkey)。第一个 HRegion 的 startkey 为空,最后一个 HRegion 的 endkey 为空。客户端可以通过 HMaster 节点快速定位每个 rowkey 所在的 HRegion。

在这里插入图片描述

Store

一个 Store 存储 HBase 表的一个列族的数据。由于表被水平分割成多个 HRegion,那么一个 HRegion 中包含一个或者多个 Store。Store 包含一个 MemStore 和多个 HFile 文件。MemStore 相当于一个内存缓冲区,数据存入磁盘之前先存入 MemStore 中。当 MemStore 中的数据大小达到一定值后,会生成一个 HFile 文件,MemStore 中的数据会转移到 HFile 文件中。StoreFile 是对 HFile 文件的封装,HFile 是 HBase 底层的数据存储格式,最终数据以 HFile 的格式存储在 HDFS 中。

值得注意的是,一个HFile 文件只存放某个时刻 MemStore 中的所有数据,一个完整的行数据可能存放于多个 HFile 中。

HLog

HLog 是 HBase 的日志文件,存储于 HDFS 中,用于记录数据的写操作。HBase 在写入数据时会先进行 WAL(预写日志)操作,即将写操作写入到 HLog 文件中,才会将数据写入 Store 的 MemStore 中,只有这两个地方都写入并且确认后,才认为数据写入成功。

ZooKeeper

每个 HRegionServer 节点会在 ZooKeeper 中注册一个自己的临时节点,HMaster 通过这些临时节点发现可用的 HRegionServer 节点,跟踪 HRegionServer 节点的故障等。

HBase 利用 ZooKeeper 确保只有一个活动的 HMaster 节点在运行。

HRegion 应该分配到哪个 HRegionServer 节点上,也是通过 ZooKeeper 得知的。

客户端操作

创建表

public class CreateTableDemo {

    public static void main(String[] args) throws IOException {
        Configuration configuration = HBaseConfiguration.create();
      	// 指定ZooKeeper集群地址
        configuration.set("hbase.zookeeper.quorum", "10.211.55.6:2181,10.211.55.7:2181,10.211.55.8:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);
        Admin admin = connection.getAdmin();
        TableName tableName = TableName.valueOf("t_order3");
      	// 创建表描述
        HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
      	// 创建列描述
        HColumnDescriptor hColumnDescriptor = new HColumnDescriptor("f1");
      	// 添加列族
        hTableDescriptor.addFamily(hColumnDescriptor);
      	// 创建表
        admin.createTable(hTableDescriptor);
    }
}

接下来进入 HBase shell 命令行模式,执行 list 命令查看当前所有表。

TABLE                                                                           
t_order                                                                         
t_order2                                                                        
t_order3                                                                        
3 row(s)
Took 0.0261 seconds                                                             
=> ["t_order", "t_order2", "t_order3"]

可见,t_order3 表已经创建成功了。

此外也可以执行 create 命令创建表。create 命令指定表名、列族。

hbase:004:0> create 't_order4', 'f1'
Created table t_order4
Took 1.3553 seconds                                                             
=> Hbase::Table - t_order4

添加数据

public class AddDataDemo {

    public static void main(String[] args) throws IOException {
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "10.211.55.6:2181,10.211.55.7:2181,10.211.55.8:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);
        TableName tableName = TableName.valueOf("t_order2");
        Table table = connection.getTable(tableName);
        // 设置行键
        Put put = new Put(Bytes.toBytes("row1"));
        // 添加列族、列名、列值
        put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("name"), Bytes.toBytes("zhangsan2"));
        put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("age"), Bytes.toBytes("20"));
        put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("address"), Bytes.toBytes("beijing"));
        table.put(put);
        // 设置行键
        Put put2 = new Put(Bytes.toBytes("row2"));
        // 添加列族、列名、列值
        put2.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("name"), Bytes.toBytes("lisi"));
        put2.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("age"), Bytes.toBytes("25"));
        put2.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("address"), Bytes.toBytes("shanghai"));
        table.put(put2);
        // 释放资源
        table.close();
    }
}

接下来进入 HBase shell 命令行模式,执行 scan 命令扫描 t_order2 表的所有数据。

hbase:006:0> scan 't_order2'
ROW                   COLUMN+CELL                                               
row1                  column=f1:address, timestamp=2023-04-15T21:37:23.457, value=shanghai                                                
row1                  column=f1:age, timestamp=2023-04-15T21:37:23.457, value=25
row1                  column=f1:name, timestamp=2023-04-15T21:37:23.457, value=lisi                                                       
row2                  column=f1:age, timestamp=2023-04-15T21:16:46.128, value=22
row2                  column=f1:name, timestamp=2023-04-15T21:16:35.289, value=wangwu                                                     
2 row(s)
Took 0.0699 seconds    

可以使用 put 命令添加数据。put 命令可以指定表名、行键、列族:列名、列值。

hbase:007:0> put 't_order2', 'row2', 'f1:name', 'wangwu'
Took 0.1126 seconds                                                             
hbase:008:0> put 't_order2', 'row2', 'f1:age', '22'
Took 0.0868 seconds     

修改数据

与添加数据的方式相同。

删除数据

public class DeleteDataDemo {

    public static void main(String[] args) throws IOException {
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "10.211.55.6:2181,10.211.55.7:2181,10.211.55.8:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);
        // 指定表名
        TableName tableName = TableName.valueOf("t_order2");
        Table table = connection.getTable(tableName);
        // 指定行键
        Delete delete = new Delete(Bytes.toBytes("row2"));
        table.delete(delete);
        table.close();
    }
}

可以执行 delete 命令删除指定单元格。delete 命令可以指定表名、行键、列族:列名。

hbase:009:0> delete 't_order2', 'row1', 'f1:address'
Took 0.0834 seconds 

可以执行 deleteall 命令删除一整行数据。delete 命令可以指定表名、行键。

hbase:010:0> deleteall 't_order2', 'row1'
Took 0.0370 seconds    

可以执行 disabledrop 命令删除一张表。disable 命令禁用表,可以指定表名;drop 命令删除表,可以指定表名。

hbase:010:0> disable 't_order2'
Took 0.0375 seconds    
hbase:010:0> drop 't_order2'
Took 0.0375 seconds    

查询数据

public class QueryDataDemo {

    public static void main(String[] args) throws IOException {
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "10.211.55.6:2181,10.211.55.7:2181,10.211.55.8:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);
        // 指定表名
        TableName tableName = TableName.valueOf("t_order2");
        Table table = connection.getTable(tableName);
        // 指定行键
        Get get = new Get(Bytes.toBytes("row1"));
        Result result = table.get(get);
        for (Cell cell : result.rawCells()) {
            // 获取列族
            String family = new String(CellUtil.cloneFamily(cell));
            // 获取列名
            String qualifier = new String(CellUtil.cloneQualifier(cell));
            // 获取列值
            String value = new String(CellUtil.cloneValue(cell));
            System.out.println("列:" + family + ":" + qualifier + "---值:" + value);
        }
    }
}

接下来进入 HBase shell 命令行模式,执行 get 命令查询一整行数据。get 命令可以指定表名、行键。

hbase:011:0> get 't_order2', 'row1'
COLUMN                CELL                                                      
 f1:address           timestamp=2023-04-15T21:10:16.950, value=shanghai         
 f1:age               timestamp=2023-04-15T21:10:16.950, value=25               
 f1:name              timestamp=2023-04-15T21:10:16.950, value=lisi             
1 row(s)
Took 0.0684 seconds  

可以执行 count 命令获取表的记录数。count 命令可以指定表名。

hbase:001:0> count 't_order2'
2 row(s)
Took 0.5480 seconds                                                             
=> 2

可以执行 exists 命令查看表是否存在。exists 命令可以指定表名。

hbase:008:0> exists 't_order2'
Table t_order2 does exist                                                       
Took 0.2278 seconds                                                             
=> true
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。