mdb存储引擎实现

来源:互联网 发布:艾琳和后羿的伤害数据 编辑:程序博客网 时间:2024/06/01 10:50

存储引擎整体结构

Memory pool


如图所示,mdb存储引擎针对一块大的内存进行操作,内存引擎实现的原理如下:

初始化

  • 首先分配一块大的内存,然后初始mem_pool对象,为mem_pool_impl对象中相关page的元数据赋值,如page_size、total_pages、meta_pages、free_pages、current_page等。
  • 初始化mem_cache对象,初始化cache_info元数据对象,初始化cache_info中max_slab_id、base_size、factor相关成员。
  • 初始化mem_cache对象中slab_manager对象数组,slab_manager数组大小为cache_info对象中max_slab_id,每个slab_manager还包括一个partial_pages的数组,数组的大小为该slab管理的page中item数决定,计算方式为: ( item数+(10-1)/10 )。

内存分配

  • 分配指定大小的内存时,会为其在slab_manager数组中选择一个合适的slab(管理的长度大于指定的内存大小的最小的slab)。
for (size_t i = 0; i < slab_managers.size(); ++i){    if (slab_managers[i]->slab_size >= size)    {       mgr = slab_managers[i];       break;    }}
  • 通过获取的slab分配内存,内存分配的顺序为首先判断是否有partial_pages_no,如果有则直接从部分可用的page中分配item,如果没有部分可用的page,则直接判断是否有全空闲的page,如果有,则从全空闲的page中分配item,如果上述两种情况都没有,则从mem_pool中分配并初始化一块空闲的page,分配item,并且将该page连接到该slab对应的部分可用的page中。

    说明:
    在通过slab成功分配了item后,会将item连接到hashtable的链表中,具体的连接方式我们在后面的hashtable一节中详细描述,通过hashtable管理分配后的item的好处是提高查找的效率,减少相应操作的时间。

  • 删除记录操作时,实际是调用存储引擎的删除item的接口,同分配内存一样,也是根据item的大小,确定对应管理该内存的slab,删除的主要操作包括:
    1)将该item从hashtable的链表中删除。
    2)获取该item所在页,根据该页当前的状态是全满还是部分满,更新释放该item后的page状态,对于部分空闲的page,还需要根据空闲的item数重新将该page连接到对应的部分空闲队列上。

Hashtable:


这里主要介绍hashtable对应链表中的插入及删除操作,hashtable中每一个bucket的类型为uint64_t数据类型,对应item的id。
item初始化:

for(int i = 0; i < per_slab; ++i){     item->next = item->prev = 0;    item->item_id = ITEM_ID(slab_size, i, index, slab_id);    item->h_next = ITEM_ID(slab_size, i + 1, index, slab_id);    item = reinterpret_cast<mdb_item *>((char*)item + slab_siz);}

item相关主要操作:

#define ITEM_KEY(it) (&((it)->data[0]))#define ITEM_DATA(it) (&((it)->data[0]) + (it)->key_len)#define ITEM_AREA(it) (((it)->data[0]&0xff)|(((it)->data[1]<<8)&0xff00))#define KEY_AREA(key) ((key[0]&0xff)|((key[1]<<8)&0xff00))#define ITEM_ID(_slab_size,_offset,_page_id,_slab_id)   \    ({                                                   \        (((uint64_t)_slab_id) << 52                 \         |((uint64_t)_page_id)<<36                  \         |((uint64_t)_offset) << 20                  \         |((uint64_t)_slab_size));})#define ITEM_ADDR(base,item_id,page_size)                               \    ({assert(item_id != 0);                                              \        reinterpret_cast<mdb_item *>(base                                 \                                     + PAGE_ID(item_id) * page_size       \                                     + SLAB_SIZE(item_id) * PAGE_OFFSET(item_id)                                     + sizeof(mem_cache::page_info));})#define id_to_item(id)                                                  \    ITEM_ADDR(this_mem_pool->get_pool_addr(),id,mdb_param::page_size )

item插入hashtable:
插入时,先将item映射到某一个bucket上,然后将该bucket上的itemid赋值给item->next,然后将item的itemid赋值给该bucket的第一过程就是一个链表第一个元素的入过程,时间复杂度为o(1)。

int idx = get_bucket_index(item);item->h_next = hashtable[idx];hashtable[idx] = item->item_id;

hashtable中删除item:
删除时,先通过find函数遍历链表,根据遍历查找的结果,判断要删除的节点是否存在、是链首还是非链首位置,根据对应的结果从链表中删除item节点。

int idx = get_bucket_index(item);uint64_t pos = hashtable[idx];mdb_item *prev = 0;mdb_item *pprev = 0;prev = __find(pos, ITEM_KEY(item), item->key_len, &pprev);if(prev == 0)                  // not found{    return false;}if(pprev){    pprev->h_next = prev->h_next;}else{    hashtable[idx] = prev->h_next;}prev->h_next = 0;
原创粉丝点击