在HBase中,sequenceId是实现一致性的灵魂,这里的一致性有两方面意思:
- 顺序性,即先写的先被读到,未提交的写不能被读到;
- 完整性,包含如下几点;
- 未持久化到hfile的数据,对应的wal文件不能删除
- 如果由于wal文件过多需要清理,则需要先把对应的数据持久化
- 异常恢复过程中,需要能够找出wal文件中尚未持久化的部分
以下从seqId的生成、保存和使用几方面对相关知识点进行了整理;
在哪里生成
1、每个HRegion都对应了一个的MultiVersionConcurrencyControl,该类中有2个变量:readPoint和writePoint,rpcHandler处理写请求的过程中,在写wal之前,会通过将writePoint加1来得到最新的seqId,然后创建1个WriteEntry,封装了该seqId和代表是否完成的状态,最后放入到1个链表中,代码如下:
synchronized (writeQueue) {
long nextWriteNumber = writePoint.incrementAndGet();
WriteEntry e = new WriteEntry(nextWriteNumber);
writeQueue.add(e);
action.run();
return e;
}
在哪些地方保存
- 写wal,walEntry的key中包含该seqId,同时会更新SequenceIdAccounting中的highestSequenceIds,其中保存了该WAL实例负责的各个region的最大seqId,值得注意的时,如果启用了multiwal,则会存在多个WAL实例并各自对应一个SequenceIdAccounting实例;
- 写memstore,cell中包含该seqId;
- 写完成时,更新readPoint为该seqId,这里会检查writeQueue中是否存在更小的未完成WriteEntry,如果有,则不会更新;
- flush,生成的hfile中,除了会在cell中包含该seqId之外,还会在fileInfo中保存MAX_SEQ_ID,代表hfile中的最大seqId,同时也会更新SequenceIdAccounting中的lowestUnflushedSequenceIds,其中保存了每个region下各个store的最小未持久化seqId;
- report,rs每隔3s会上报信息给master,其中包含了每个region及其各个store的lastFlushedSequenceIds;
- compact,会获取smallestReadPoint,seqId小于该值的cell会被当前及后续的所有scanner读取,因此在生成的新hfile中这些cell不需要再保存seqId;
- roll log,会获取SequenceIdAccounting中的highestSequenceIds,将其保存到AbstractFSWAL中的walFile2Props,然后将highestSequenceIds清空;
- bulkload,会先请求执行flush,其中会通过writePoint加1得到最新的seqId,并将其作为后缀放在hfile的文件名中(完整suffix:"_SeqId_" + seqNum + "_");
在哪些地方使用
- rpcHandler处理读请求的时候,会获取region的mvcc中最新的readpoint,以此过滤memstore和hfile中的数据;
- logRoller创建新wal文件时,会对AbstractFSWAL中的walFile2Props与SequenceIdAccounting中的lowestUnflushedSequenceIds进行比较,如果某个wal文件中各region的数据都已经落盘,则将其转移到oldWALs目录;
- log文件过多时,需要将最早那个log文件转移到oldWALs目录,此时也是对AbstractFSWAL中的walFile2Props与SequenceIdAccounting中的lowestUnflushedSequenceIds进行比较,找出存在未落盘数据的reigon进行flush;
- rs挂掉后执行scp,需要splitlog,将不同reigon的log放到该reigon的recovered.edits目录下,用来在open时replay,此时splitworker会从master获取seqId来过滤掉确定已落盘的数据,获取到的seqId分为region级别的lastFlushedSequenceId和store级别的storeSequenceId,分别用于读取wal文件和写入recovered.edits目录时进行过滤;
- openreigon的时候需要对recovered.edits目录下的数据进行replay,此时会再次根据store中各hfile的最大seqId进行过滤,这里再次过滤的原因是,splitlog时从master获取的seqId是rs定时上报的,存在一定的滞后;
- 另外,新引入的serial replication特性,也会使用seqId进行相关的协调控制;