一、数据存储和读取
HBase的数据存放在HDFS文件系统上,并且按照列族和行键进行组织。一行数据包含多个列族,每个列族包含多个列(也就是HBase中的列簇)。
HBase的读写流程如下:
- 客户端与HBase ZooKeeper进行连接,并获取到表的位置信息。
- 客户端向HRegionServer发送读/写请求,同时将请求中的行键转换为HRegion的位置信息。
- HRegionServer在本地缓存中查找数据。
- 如果缓存中没有数据,则根据HRegion的位置信息,向HDFS中的数据块获取数据。
- 如果客户端发送的是写请求,HRegionServer将数据保存到内存中,并异步地将数据刷写到HDFS。
- 如果客户端发送的是读请求,HRegionServer将数据返回给客户端。
上述流程中,客户端与ZooKeeper的交互是通过ZooKeeper中相应节点的监听事件驱动的。所有读写请求都先发送给HRegionServer,然后由HRegionServer根据请求的类型分发给对应的HRegion进行操作。
二、写入数据
接下来将分别介绍写入新数据和更新已有数据两种情况下的HBase写入流程。
写入新数据
写入新数据时,HBase的写入流程如下:
- 客户端构建Put对象,选择列族和行键,添加包含具体列值的键值对。
- 客户端与ZooKeeper交互,获取表位置信息。
- 客户端向HRegionServer发送写请求,写请求中包括表的位置信息、Put对象的数据和操作类型。
- HRegionServer接收到写请求后,将数据保存到内存中。
- 随着内存中的数据量逐渐积累,HRegionServer会将数据写入HDFS中。
- 在HDFS中生成新的HFile文件。
- 当HFile文件大小达到合适的大小时,HRegionServer会将多个HFile文件进行合并,生成一个更大的HFile文件。
Put put = new Put(Bytes.toBytes("row1"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("name"), Bytes.toBytes("Tom"));
Configuration config = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(config);
Admin admin = connection.getAdmin();
TableName tableName = TableName.valueOf("testtable");
HTableDescriptor tableDescriptor = new HTableDescriptor(tableName);
HColumnDescriptor columnDescriptor = new HColumnDescriptor("cf");
tableDescriptor.addFamily(columnDescriptor);
admin.createTable(tableDescriptor);
Table table = connection.getTable(tableName);
table.put(put);
更新已有数据
更新已有数据时,HBase的写入流程如下:
- 客户端构建Put对象,并添加需要更新的具体列值。
- 客户端与ZooKeeper交互,获取表位置信息。
- 客户端向HRegionServer发送写请求,请求中包含数据、表的位置信息和操作类型。
- HRegionServer根据请求中的行键,找到要更新的数据。
- HRegionServer将新的数据更新到内存中。
- 随着内存中的数据量逐渐积累,HRegionServer会将数据写入到HDFS中。
- 在HDFS中生成新的HFile文件。
- 当HFile文件大小达到合适的大小时,HRegionServer会将多个HFile文件进行合并,生成一个更大的HFile文件。
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("age"), Bytes.toBytes("30"));
三、读取数据
接下来将分别介绍获取指定行和范围扫描两种情况下的HBase读取数据流程。
获取指定行数据
获取指定行的数据时,HBase的读取流程如下:
- 客户端构造Get对象,设置列族和行键。
- 客户端与ZooKeeper交互,获取表位置信息。
- 客户端向HRegionServer发送读请求,请求中包含数据、表的位置信息和操作类型。
- HRegionServer根据请求中的行键,找到要读取的数据。
- HRegionServer从内存中或者HDFS中读取数据,并返回给客户端。
Get get = new Get(Bytes.toBytes("row1"));
get.addFamily(Bytes.toBytes("cf"));
范围扫描数据
范围扫描数据时,HBase的读取流程如下:
- 客户端构造Scan对象,并设置要扫描的列族和范围。
- 客户端与ZooKeeper交互,获取表位置信息。
- 客户端向对应的HRegionServer发送范围扫描请求,请求中包含数据、表的位置信息和操作类型。
- HRegionServer查找内存中的块或者HDFS中的块,并返回数据给客户端。
- 如果需要合并多个HFile文件,HRegionServer会先进行内部合并操作,并返回给客户端。
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("row1"));
scan.setStopRow(Bytes.toBytes("row5"));
scan.addFamily(Bytes.toBytes("cf"));
四、总结
HBase的读写流程比较复杂,但是仍然是一个高效的分布式系统。对于数据量比较大的应用场景,使用HBase存储和读取数据是一个不错的选择。