HBase相对复杂,想要快速啃下来比较困难。而MiniBase吸收了HBase最核心的引擎部分的精华,希望可以通过学习MiniBase以小见大,能够对自己理解HBase这个庞然大物有所帮助。
MiniBase是一个标准的LSM树索引结构,分内存部分和磁盘部分。
从源码不难看出,其实就是2个ConcurrentSkipListMap kvMap和snapshot,flush的时候将kvMap赋值给snapshot,然后启动一个新的ConcurrentSkipListMap
DiskFile由3种类型的数据块组成,分别是DataBlock、IndexBlock、MetaBlock。
IndexBlock:一个DiskFile内有且仅有一个IndexBlock,它主要存储多个DataBlock的索引数据。每个索引数据又包含4个字段:
一个DiskFile中有且仅有一个MetaBlock;同时MetaBlock是定长的,因此可以直接通过定位diskf ile.f ilesize - len(MetaBlock)来读取MetaBlock,而无需任何索引.
假设用户需要读取指定DiskFile中key='abc'对应的value数据,那么可以按照如下流程进行IO读取
在MiniBase中,只容许两种更新操作:
private KeyValue(byte[] key, byte[] value, Op op, long sequenceId) {
assert key != null;
assert value != null;
assert op != null;
assert sequenceId >= 0;
this.key = key;
this.value = value;
this.op = op;
this.sequenceId = sequenceId;
}
KeyValue在MemStore和DiskFile中都是有序存放的,所以需要为KeyValue实现Comparable接口,如下所示:
@Override
public int compareTo(KeyValue kv) {
if (kv == null) {
throw new IllegalArgumentException("kv to compare should not be null");
}
int ret = Bytes.compare(this.key, kv.key);
if (ret != 0) {
return ret;
}
if (this.sequenceId != kv.sequenceId) {
return this.sequenceId > kv.sequenceId ? -1 : 1;
}
if (this.op != kv.op) {
return this.op.getCode() > kv.op.getCode() ? -1 : 1;
}
return 0;
}
写入过程需要构造一个kv结构(put/delete),并加上一个自增的sequenceId.详见MStore#put
@Override
public void put(byte[] key, byte[] value) throws IOException {
this.memStore.add(KeyValue.createPut(key, value, sequenceId.incrementAndGet()));
}
(MiniBase是本地生成,HBase应该要由服务端生成?)
kv数据是写到kvMap中,其中kvMap就是ConcurrentSkipListMap结构。这里我们需要关注:
这里详见:MemStore#add
public void add(KeyValue kv) throws IOException {
// add前需要阻塞flush
flushIfNeeded(true);
updateLock.readLock().lock();
try {
KeyValue prevKeyValue;
// ConcurrentSkipListMap特性 put后的返回值 the old value, or null if newly inserted
if ((prevKeyValue = kvMap.put(kv, kv)) == null) {
// 之前kv不存在则直接加
dataSize.addAndGet(kv.getSerializeSize());
} else {
// 之前kv存在,需要计算差值(可能有更新)
dataSize.addAndGet(kv.getSerializeSize() - prevKeyValue.getSerializeSize());
}
} finally {
updateLock.readLock().unlock();
}
flushIfNeeded(false);
}
写入MemStore后,当数据量达到一定阈值就要将flush到DiskStore
读取流程相对要复杂很多。
我们需要从多个有序集合中读取数据:
在Scan的时候,需要把多个有序集合通过多路归并算法合并成一个有序集合,然后过滤掉不符合条件的版本,将正确的KV返回给用户。
以上图为例,我们要将上面7个KV数据再次处理得到最终的结果。
对于同一个Key的不同版本,我们只关心最新的版本。假设用户读取时能看到的sequenceld≤101的数据,那么读取流程逻辑如下:
对于全表扫描的scan操作,MiniBase将返回(B,Put,101)和(C,Put,95)这两个KeyValue给用户。
详情见: MStore#ScanIter
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有