HBase系列-HBase读写流程
内容整理自:
写数据流程
客户端写流程
用户提交put请求后,HBase客户端会根据设置
autoflush=true/false(默认为true)
判断是否直接提交给服务器进行处理,如果为false
则会将请求添加到本地buffer
, 超过一定阈值(默认为2M,可以通过配置文件配置)之后才会提交。这样可以提高写入性能,但会存在因为客户端崩溃导致请求丢失的情况在提交之前,HBase会在
元数据表.meta.
中根据rowkey找到它们归属的region server,这个定位的过程是通过HConnection的locateRegion
方法获得的。如果是批量请求的话还会把这些rowkey按照HRegionLocation
分组,每个分组可以对应一次RPC请求HBase会为每个HRegionLocation构造一个远程RPC请求
MultiServerCallable<Row>
,然后通过rpcCallerFactory.<MultiResponse> newCaller()
执行调用,忽略掉失败重新提交和错误处理,客户端的提交操作到此结束
服务器端写流程
服务器端Region Server接收到客户端的写入请求后,首先会反序列化为Put对象
, 然后检查region是否是只读
、memstore大小是否超过blockingMemstoreSize
等检查操作,然后执行以下核心操作:
获取行锁、Region更新共享锁
: HBase中使用行锁保证对同一行数据的更新都是互斥操作,用以保证更新的原子性,要么更新成功,要么失败。开始写事务
:获取write number,用于实现MVCC,实现数据的非锁定读,在保证读写一致性的前提下提高读取性能。写缓存memstore
:HBase中每列族都会对应一个store,用来存储该列数据。每个store都会有个写缓存memstore,用于缓存写入数据。HBase并不会直接将数据落盘,而是先写入缓存,等缓存满足一定大小之后再一起落盘。Append HLog
:HBase使用WAL机制保证数据可靠性,即首先写日志再写缓存,即使发生宕机,也可以通过恢复HLog还原出原始数据。该步骤就是将数据构造为WALEdit对象,然后顺序写入HLog中,此时不需要执行sync操作。0.98版本采用了新的写线程模式实现HLog日志的写入,可以使得整个数据更新性能得到极大提升。释放行锁以及共享锁
Sync HLog
:HLog真正sync到HDFS,在释放行锁之后执行sync操作是为了尽量减少持锁时间,提升写性能。如果Sync失败,执行回滚操作将memstore中已经写入的数据移除。结束写事务
:此时该线程的更新操作才会对其他读请求可见,更新才实际生效。flush memstore
:当写缓存满64M之后,会启动flush线程将数据刷新到硬盘。当HBase中的memstore数据flush到磁盘的时候,就会形成一个storefile,当storefile的数量达到一定程度的时候,就需要将storefile文件进行compaction操作
,Compact作用:合并文件、清除过期,多余版本数据、提高读写效率
WAL持久化等级
SKIP_WAL
: 只写缓存,不写HLog,因为只用内存,性能很好,但容易丢失数据,不推荐ASYNC_WAL
: 异步将数据写入HLog日志中SYNC_WAL
: 同步将数据写入日志文件中,需要注意的是数据只是被写入文件系统中,并没有真正落盘FSYNC_WAL
: 同步将数据写入日志文件并强制落盘。最严格的日志写入等级,可以保证数据不会丢失,但是性能相对比较差USER_DEFALUT
: 默认如果用户没有指定持久化等级,HBase使用SYNC_WAL等级持久化数据
用户可以通过客户端设置WAL持久化等级,代码:put.setDurability(Durability. SYNC_WAL );
读流程
客户端首次读写 HBase 上数据的流程:
- 客户端从 Zookeeper 获取
META
表所在的 Region Server; - 客户端访问
META
表所在的 Region Server,从META
表中查询到访问行键所在的 Region Server,之后客户端将缓存这些信息以及META
表的位置; - 客户端从行键所在的 Region Server 上获取数据。
如果再次读取,客户端将从缓存中获取行键所在的 Region Server。这样客户端就不需要再次查询 META
表,除非 Region 移动导致缓存失效,这样的话,则将会重新查询并更新缓存。
注:META
表是 HBase 中一张特殊的表,它保存了所有 Region 的位置信息,META 表自己的位置信息则存储在 ZooKeeper 上。
更为详细读取数据流程参考:
HBase查询方式
全表查询:
scan tableName
基于rowkey的单行查询:
get tableName,'1'
基于rowkey的范围扫描:
scan tableName, {STARTROW=>'1',STOPROW=>'2'}
setCache()和setBatch()方法
Cache设置了服务器一次返回的行数,而Batch设置了服务器一次返回的列数