大白话解析LevelDB: TwoLevelIterator

文章详细解释了TwoLevelIterator在LevelDB中如何遍历SST文件,涉及IndexBlock和DataBlock的层级结构,以及Iterator接口的实现。主要介绍了构造函数、Seek方法和其在遍历过程中的作用。
摘要由CSDN通过智能技术生成

TwoLevelIterator

TwoLevelIterator其实就是用于遍历SST里所有Key-ValueIterator

那为什么要叫做TwoLevelIterator而不叫SSTIterator呢?TwoLevel指的是哪两个Level呢?

TwoLevel不是指同时遍历两层Level,而是指SST里的Index BlockData Block两层。

SST的结构如下:

+---------------------+
|   Data Block 1      |
+---------------------+
|   Data Block 2      |
+---------------------+
|        ...          |
+---------------------+
|   Data Block N      |
+---------------------+
|   Meta Block 1      |
+---------------------+
|        ...          |
+---------------------+
|   Meta Block K      |
+---------------------+
| Metaindex Block     |
+---------------------+
|   Index Block       |
+---------------------+
|      Footer         |
+---------------------+

SSTKey-Value分散在多个Data Block里,Index Block里存储每个Data BlockKey范围和在SST文件中的偏移量。

Index Block的内容如下:

+--------------------------------------------------+
| Key1 | Block Handle1 (指向第一个 Data Block 的信息) |
+--------------------------------------------------+
| Key2 | Block Handle2 (指向第二个 Data Block 的信息) |
+--------------------------------------------------+
| Key3 | Block Handle3                             |
+--------------------------------------------------+
| ...............                                  |
+--------------------------------------------------+
| KeyN | Block HandleN                             |
+--------------------------------------------------+

Block Handle1里包含了Data Block 1SST文件中的偏移量和大小。

想象一下我们现在要查找一个Key-X,需要先到Index Block中通过二分查找的方式找到对应的Block Handle,然后通过这个Block Handle找到对应的Data Block,最后再到Data Block中查找这个Key-X

所以每次查找我们都要先到Index Block中查找,然后再到Data Block中查找,这就是TwoLevelIteratorTwoLevel

Iterator 接口

TwoLevelIteratorIterator接口的一种实现,所以我们先看下Iterator里都有哪些接口需要实现:

class LEVELDB_EXPORT Iterator {
   public:
    Iterator();

    Iterator(const Iterator&) = delete;
    Iterator& operator=(const Iterator&) = delete;

    virtual ~Iterator();

    // 判断迭代器当前所在位置是否有效,如果有效,
    // 则可以通过 key() 和 value() 获取当前键值对。
    virtual bool Valid() const = 0;

    // 将当前位置移动到第一个 Key-Value 所在处。
    virtual void SeekToFirst() = 0;

    // 将当前位置移动到最后的 Key-Value 所在处。
    virtual void SeekToLast() = 0;

    // 将当前位置移动到第一个大于等于 target 的 Key-Value 所在处。
    virtual void Seek(const Slice& target) = 0;

    // 将当前位置移动到下一个 Key-Value 所在处。
    virtual void Next() = 0;

    // 将当前位置移动到上一个 Key-Value 所在处。
    virtual void Prev() = 0;

    // 返回当前位置的 Key。
    virtual Slice key() const = 0;

    // 返回当前位置的 Value。
    virtual Slice value() const = 0;

    // 返回迭代器的当前状态。
    // 如果状态不是 ok,则说明迭代器已经失效,不可使用了。
    virtual Status status() const = 0;

    // 用户可注册多个 CleanupFunction,当迭代器被销毁时,会按顺序调用这些 
    // CleanupFunction。
    // 需要注意的是,RegisterCleanup 这个方法不需要 Iterator 的子类实现,
    // Iterator 已经实现了,用户不需要重写。
    using CleanupFunction = void (*)(void* arg1, void* arg2);
    void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2);
};

TwoLevelIterator 的实现

TwoLevelIteratorIterator接口的一种实现,它需要实现Iterator接口里的所有抽象方法。

TwoLevelIterator 的构造函数

TwoLevelIterator::TwoLevelIterator(Iterator* index_iter, BlockFunction block_function, void* arg,
                                   const ReadOptions& options)
    : block_function_(block_function),
      arg_(arg),
      options_(options),
      index_iter_(index_iter),
      data_iter_(nullptr) {}

TwoLevelIterator的构造函数挺简单的,对一些成员变量进行了初始化。

主要是block_function_这个参数,它是一个函数指针,用于获取Data BlockIterator

我们来看下BlockFunction block_function的定义:

typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&);

BlockFunction是一个函数指针,指向一个用于创建Data BlockIterator的函数。

TwoLevelIterator需要创建一个新的Data BlockIterator时,它会调用block_function_。例如,当index_iter_移动到下一处了,TwoLevelIterator需要获取对应的Data Block更新data_ter,这时,它会调用block_function_

我们来看下TwoLevelIterator是如何使用block_function_的。

TwoLevelIterator::InitDataBlock
void TwoLevelIterator::InitDataBlock() {
    if (!index_iter_.Valid()) {
        // 如果 index_iter_ 无效,那对应的 data_iter_ 也是无效的。
        SetDataIterator(nullptr);
    } else {
        // 从 index_iter 中取出对应的 data_block 的 handle。
        Slice handle = index_iter_.value();
        if (data_iter_.iter() != nullptr && handle.compare(data_block_handle_) == 0) {
            // 与 handle 对应的 data_iter_ 已经构建好了,
            // 不需要重复构建。
        } else {
            // 通过 block_function_ 构建 与 handle 对应的 data_iter_。
            Iterator* iter = (*block_function_)(arg_, options_, handle);
            data_block_handle_.assign(handle.data(), handle.size());
            SetDataIterator(iter);
        }
    }
}

TwoLevelIterator::InitDataBlock中,会从index_iter_中取出对应的data_blockhandle,然后通过block_function_构建与handle对应的data_iter_

SetDataIterator(iter)就是用于设置data_iter_的。

void TwoLevelIterator::SetDataIterator(Iterator* data_iter) {
    if (data_iter_.iter() != nullptr) SaveError(data_iter_.status());
    data_iter_.Set(data_iter);
}

TwoLevelIterator::Seek(const Slice& target)

void TwoLevelIterator::Seek(const Slice& target) {
    // index_iter_ 对应着 index_block,到 index_block 中
    // 查找 target 属于哪个 data_block。
    index_iter_.Seek(target);
    // 如果找到了这个 data_block,就从 SST 中加载这个 data_block。
    InitDataBlock();
    // data_iter_.iter() != nullptr 从 SST 中加载到 target
    // 所属的 data_block了。
    if (data_iter_.iter() != nullptr) data_iter_.Seek(target);
    // 跳过不包含数据的 data_block。
    SkipEmptyDataBlocksForward();
}

target是我们要查找的Key,它存储在SSTData Block中。

要找到这个Data Block,需要先通过index_iter_Index Block里查找target属于哪个Data Block,找到这个Data Blockdata_iter,然后通过data_iterData Block中查找target

现在我们应该看出为什么要叫做TwoLevelIterator了,指的就是index_iterdata_iter这两个Iterator

index_iter.Seek(target)的实现可移步参考大白话解析LevelDB: Block Iterator

data_iter.Seek(target)的实现可移步参考大白话解析LevelDB: Block Iterator

SkipEmptyDataBlocksForward()的实现如下:

void TwoLevelIterator::SkipEmptyDataBlocksForward() {
    while (data_iter_.iter() == nullptr || !data_iter_.Valid()) {
        // index_iter_ 无效,表示 index_block 已经遍历完了。
        // 此时将 data_iter_ 置为 null 表示 data_iter_ 已经
        // 到末尾了。
        if (!index_iter_.Valid()) {
            SetDataIterator(nullptr);
            return;
        }
        // index_iter_ 里还有东西,继续往后走一位。
        index_iter_.Next();
        // 加载与当前 index_iter_ 对应的 Data Block。
        InitDataBlock();
        // 如果 data_iter_ 有效,就将 data_iter_ 移到第一个有效的位置。
        if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst();
    }
}

如果index_iter_对应的data_iter_里没有有效数据,就将index_iter_往后移一位, 加载下一个data_block。一直到找到有数据的data_block为止。

TwoLevelIterator::SeekToFirst

void TwoLevelIterator::SeekToFirst() {
    // 到 index_block 中查找第一个 data_block 是哪个。
    index_iter_.SeekToFirst();
    // 从 SST 中加载这个 data_block。
    InitDataBlock();
    // data_iter_.iter() != nullptr 从 SST 中加载到第一个有效的
    // data_block 了。
    if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst();
    // 跳过不包含数据的 data_block。
    SkipEmptyDataBlocksForward();
}

TwoLevelIterator::SeekToFirstTwoLevelIterator::Seek的逻辑大体一样,先通过index_iter找到对应的data_iter,然后再通过data_iter定位data_block里的第一对Key-Value

不过有同学可能会对if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst()里的if (data_iter_.iter() != nullptr)判断有疑惑,难道SST里全是无效的Data Block

假设我们不停的对LevelDB做删除操作,db->Delete(leveldb::WriteOptions(), key),那某个SST里的Key就全是Delete类型的,这时候我们对这个SSTSeekToFirst操作,就找不到任何有效的Data Block

TwoLevelIterator::SeekToLast

TwoLevelIterator::SeekToLast的逻辑和TwoLevelIterator::SeekToFirst一样,就不再赘述啦。

void TwoLevelIterator::SeekToLast() {
    // 到 index_block 中查找最后一个 data_block 是哪个。
    index_iter_.SeekToLast();
    // 从 SST 中加载这个 data_block。
    InitDataBlock();
    // data_iter_.iter() != nullptr 从 SST 中加载到第一个有效的
    // data_block 了。
    if (data_iter_.iter() != nullptr) data_iter_.SeekToLast();
    // 跳过不包含数据的 data_block。
    SkipEmptyDataBlocksBackward();
}

TwoLevelIterator::Next

void TwoLevelIterator::Next() {
    assert(Valid());
    // 将 data_iter_ 往后移一位即可。
    data_iter_.Next();
    // 跳过不包含数据的 data_block。
    SkipEmptyDataBlocksForward();
}

TwoLevelIterator::Next就不需要用到index_iter_了,将data_iter_按顺序往后移一位即可。

TwoLevelIterator::Prev

void TwoLevelIterator::Prev() {
    assert(Valid());
    // 将 data_iter_ 往前移一位即可。
    data_iter_.Prev();
    // 跳过不包含数据的 data_block。
    SkipEmptyDataBlocksBackward();
}

TwoLevelIterator::Next同理,不需要用到index_iter_,将data_iter_按顺序往前移一位即可。

TwoLevelIterator::key

Slice key() const override {
    assert(Valid());
    // 取出 data_iter_ 当前位置的 key。
    return data_iter_.key();
}

取出data_iter_当前位置的key即可。

TwoLevelIterator::value

Slice value() const override {
    assert(Valid());
    // 取出 data_iter_ 当前位置的 value。
    return data_iter_.value();
}

取出data_iter_当前位置的value即可。

TwoLevelIterator::status

Status status() const override {
    if (!index_iter_.status().ok()) {
        // 先检查 index_iter_ 是否有异常
        return index_iter_.status();
    } else if (data_iter_.iter() != nullptr && !data_iter_.status().ok()) {
        // 再看 data_iter_ 是否有异常
        return data_iter_.status();
    } else {
        // index_iter_ 和 data_iter_ 都没异常,
        // 返回 TwoLevelIterator 自己的状态信息。
        return status_;
    }
}

先检查index_iterdata_iter_是否有异常,如果有的话,先返回它们的异常信息。

如果index_iterdata_iter_都没有异常,就返回TwoLevelIterator自己的状态信息。

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值