HBase 的数据存储在表中。表名是一个字符串。表由行和列组成。
HBase 的行由行键(rowkey)和 n 个列(column)组成。行键没有数据类型,可以看作是字节数组,类似于关系型数据库的主键索引,在整个 HBase 表中是唯一的,按照字母顺序排序。
HBase 的列族由多个列组成,相当于将列进行分组。列的数量没有限制。表中的每一行都有同样的列族。列族必须在表创建的时候指定,不能轻易修改,并且数量不能太多,一般不超过 3 个。列族名的类型是字符串。
列限定符用于代表 HBase 表中列的名称,列族中的数据通过列限定符来定位,常见的定位格式为 “family:qualifier”(比如定位列族 cf1 的列 name,则使用 cf1:name)。一个列族下面可以有多个列限定符。列限定符没有数据类型,可以看作是字节数组。
单元格通过行键、列族、列限定符一起来定位。单元格包括值和时间戳。值没有数据类型,总是视为字节数组。时间戳代表该值的版本,类型为 long。默认,时间戳表示数据写入服务器的时间,但是当数据放入单元格时,也可以指定不同的时间戳。每个单元格都根据时间戳保存着同一份数据的多个版本,并且按照降序排列,即最新的数据排在前面。对单元格中的数据进行访问的时候会默认读取最新值。
{
"00001": { // 行键
"info": { // 列族
"username": { // 列限定符
"15335401223674": "zhangsan" // 时间戳:列值
},
"password": { -----
"1533540265719": "hello", |
"1533540102020": "123" | --> 单元格
} -----
}
}
}
HMaster 节点可以有多个。通过 ZooKeeper 的选举机制保证同一时刻只有一个 HMaster 节点处于活动状态,其它 HMaster 节点处于备用状态。
HMaster 节点的特点如下:
HBase 通过 rowkey 自动将表水平切分成多个区域,这个区域称为 HRegion。每个 HRegion 由表中的多行数据组成。
最初一个表只有一个 HRegion,随着数据的增多,当数据大到一定的值后,便会在某行的边界上将表分割成两个大小基本相同的HRegion。然后由 HMaster 节点将不同的 HRegion 分配到不同的 HRegionServer 节点上,由 HRegionServer 节点对其进行管理以及响应客户端的读写请求。换言之,分布在集群中的所有 HRegion 按序排列就组成了一张完整的表。
每个 HRegion 记录了 rowkey 的起始行键(startkey)、结束行键(endkey)。第一个 HRegion 的 startkey 为空,最后一个 HRegion 的 endkey 为空。客户端可以通过 HMaster 节点快速定位每个 rowkey 所在的 HRegion。
一个 Store 存储 HBase 表的一个列族的数据。由于表被水平分割成多个 HRegion,那么一个 HRegion 中包含一个或者多个 Store。Store 包含一个 MemStore 和多个 HFile 文件。MemStore 相当于一个内存缓冲区,数据存入磁盘之前先存入 MemStore 中。当 MemStore 中的数据大小达到一定值后,会生成一个 HFile 文件,MemStore 中的数据会转移到 HFile 文件中。StoreFile 是对 HFile 文件的封装,HFile 是 HBase 底层的数据存储格式,最终数据以 HFile 的格式存储在 HDFS 中。
值得注意的是,一个HFile 文件只存放某个时刻 MemStore 中的所有数据,一个完整的行数据可能存放于多个 HFile 中。
HLog 是 HBase 的日志文件,存储于 HDFS 中,用于记录数据的写操作。HBase 在写入数据时会先进行 WAL(预写日志)操作,即将写操作写入到 HLog 文件中,才会将数据写入 Store 的 MemStore 中,只有这两个地方都写入并且确认后,才认为数据写入成功。
每个 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
可以执行 disable
、drop
命令删除一张表。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