MOT(4)--Masstree

引言

在上一篇博客前面的MOT概述中,确定了接下来的研究方向。本篇博客将会开始这个系列的第一个目标——对mass tree索引的解析

本篇博客参考了论文Eurosys, Cache craftiness for fast multicore key-value storage 以及论文作者实现的代码

一、masstree_index.h

文件内容是Masstree主要索引接口,其下声明并实现了一个虚基类—MasstreePrimaryIndex,这个类继承于Index类。 他的结构体成员变量default_table_params **使用32位对齐。这个变量包含值类型,线程类型,值输出类型三个信息。 同时别名定义了三个类型,方便成员函数的使用,让使用逻辑更加清晰。 他的另一个结构体成员变量MTIterator **,是一个用于多线程迭代器的类,它同样对几个变量类型进行了别名定义,值得注意的是,使用的private定义,让这种定义只能由该类使用,一方面限定了使用方式,另一方面防止了不同作用域的使用混乱问题。

  • 他的成员函数首先声明了获得索引大小和MASSTREE大小的两个函数

  • 然后实现了内存池的基本操作:初始化,摧毁,状态输出,获取子结点内存池状态,获取插入结点内存池状态

  • 再然后是关于locate(加载位置)的一系列函数,主要为静态函数。

  • 还有主要是围绕索引接口的声明,这些接口在mass tree_index.cpp中将会被实现,在本篇博客的下半段举例说明。

文件的具体结构如下图所示

接下来我们文件中较为典型的一个静态函数DeallocateFromPoolCallBack的代码进行分析 源码以及注释如下:

 
  1. static uint32_t DeallocateFromPoolCallBack(void* pool, void* ptr, bool dropIndex)
  2. {
  3. // If dropIndex == true, all index's pools are going to be cleaned, so we skip the release here
  4. ObjAllocInterface* localPoolPtr = (ObjAllocInterface*)pool;
  5. if (dropIndex == false) {
  6. localPoolPtr->Release(ptr);
  7. }
  8. return localPoolPtr->m_size;
  9. }
  • 将 pool 参数强制转换为 ObjAllocInterface* 类型的指针,并将结果存储在 localPoolPtr 变量中。这个转换似乎是为了将 pool 参数解释为一个对象池接口的指针,以便后续调用对象池的 Release 方法。

  • 检查 dropIndex 参数的值。如果 dropIndex 的值为 false,则执行条件块内的代码。在这个条件块内,它调用 localPoolPtr 指向的对象池的 Release 方法,用于释放 ptr 指向的内存块。

  • 最后,函数返回了 localPoolPtr 指向的对象池的 m_size 成员变量的值 这个静态函数使用void接收传入的指针参数,巧妙地解决了类型转换问题,void可以接受所有32位的指针类型。

二、masstree_index.cpp

文件在定义的MOT命名空间首部使用了两次宏函数,前者是定义一个模板类的日志记录器,后者是非模板类的日志记录器。 下面给出后者的使用过程以及定义过程的源码:

 
  1. MPLEMENT_CLASS_LOGGER(MasstreePrimaryIndex, Storage);

使用宏函数,创建了一个MOT空间的变量

 
  1. #define IMPLEMENT_CLASS_LOGGER(Class, Component) MOT::Logger Class::_logger(#Class, #Component);

实际上是一个_logger变量 在这个文件里面,它实现了 文件的结构如下图所示masstree_index.h画的大饼——对索引的操作,其中对于Masstree的搜索,依据不同的参数,实现了两种方式。

接下来我们将会对实现了重载的search函数进行对比分析,探究哪一种更安全,为什么更安全。

字符串指针和字符串长度搜索

函数并不复杂,他先创建了一个迭代器,然后判断是否创建成功,再然后依据传入参数forward判断是何种迭代方式,再进行扫描和转换。 这段代码是一个C++成员函数 MasstreePrimaryIndex::Search() 的实现,用于在索引中执行搜索操作。以下是对这段代码的解读:

详细解释如下:

  1. 函数接受多个参数,包括:

    • keybuf:一个指向字符(char)数组的指针,表示要搜索的键值的数据。
    • keylen:一个表示键值长度的无符号整数。
    • matchKey:一个布尔值,指示是否需要精确匹配键值。
    • forward:一个布尔值,表示搜索方向,如果为 true,则表示正向搜索,如果为 false,则表示反向搜索。
    • pid:一个无符号整数,可能表示某种标识符。
    • found:一个布尔引用,用于指示是否找到了匹配的键值。
    • passive:一个布尔值,表示是否以被动方式进行搜索。
  2. 函数内部首先声明了一个指向 IndexIterator 类型的指针 itr,并将其初始化为 nullptr

  3. 接下来,根据 forward 参数的值,函数决定是执行正向搜索还是反向搜索。如果 forwardtrue,则执行以下操作:

    • 创建一个正向迭代器 IndexImpl::ForwardIterator* itrImpl
    • 如果创建失败,函数记录一条内存分配失败的错误消息,并返回 nullptr
    • 调用 m_index 对象的 iteratorScan 函数,传递了键值数据 keybuf、键值长度 keylen、匹配选项 matchKey、正向迭代器 itrImpl、以及其他参数,执行索引扫描操作。
    • 创建一个 MTIterator 对象,用于包装正向迭代器,并将其指针赋给 itr
    • 如果创建 MTIterator 失败,函数记录一条内存分配失败的错误消息,并释放先前创建的正向迭代器,然后返回 nullptr
  4. 如果 forwardfalse,则执行类似的操作,但是创建了一个反向迭代器 IndexImpl::ReverseIterator* itr_impl,并使用 MTIterator 对象包装它。

  5. 最后,函数返回指向创建的迭代器对象的指针 itr,这个迭代器将用于遍历搜索结果。

总之,这段代码是用于在 MasstreePrimaryIndex 类中执行索引搜索操作的函数。根据传入的参数,它创建了正向或反向迭代器,并将其包装在 MTIterator 对象中,然后返回这个迭代器,以便在搜索结果上进行迭代。如果内存分配失败或其他错误发生,它会返回 nullptr 并记录相应的错误消息。

函数流程图如下

Key类型指针搜索

函数实际是对前者的调用,实际上是对自己的重载函数的调用,这是一种常见的技巧性做法,函数调用其自己的重载版本通常用于提供更灵活的函数接口,减少代码重复,增强代码的可维护性,或在递归算法中解决问题。这种技术可以提高代码的清晰度和可读性,并使代码更容易维护和扩展。

其具有如下优点:
  1. 提供默认参数值:一个函数的不同版本可能有一些参数是相同的,而其他参数是不同的。通过在一个重载函数中调用另一个重载函数,可以减少代码重复,将公共参数的处理逻辑放在一个地方,而将不同的参数逻辑放在各自的重载函数中。这样,函数的用户可以选择只提供一部分参数,而其他参数使用默认值,从而简化函数调用。

  2. 减少代码重复:当多个重载函数具有相似的操作逻辑,但参数不同或有细微差异时,可以在其中一个重载函数中实现通用的操作,然后在其他重载函数中调用它,以避免重复编写相同的代码。

  3. 维护代码:如果需要对函数的操作逻辑进行更改或添加新功能,只需要在一个地方修改重载函数的代码,而不必修改所有的重载版本,从而提高了代码的可维护性。

  4. 构造器委托:在面向对象编程中,构造函数经常会调用其他构造函数来避免代码重复。这被称为构造器委托,其中一个构造函数可以调用同一类中的其他构造函数,以初始化对象的不同部分。

  5. 递归算法:在某些情况下,函数可能需要递归地调用自己来解决问题,通常用于解决可以分解为相同问题的更小的子问题的情况。这种递归调用通常需要基本情况的退出条件,以避免无限递归。

小结

在这两天的学习中,分析了mass tree所需要的索引文件,基本操作文件,了解了mass tree的建造原理。当然,笔者只对他们做了大致了解,或简单分析。不足之处还望各位同学、老师在评论区进行指正,不胜感激。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值