levelDB中的skiplist跳表 上

前言

在之前的博客中已经简要介绍了跳表的原理,接下来将介绍跳表在levelDB中的实现。
本小节主要讲一下跳表和其Node的实现结构,关于具体的方法将在下一节中讲到。

代码位置:leveldb-master/db/skiplist.h

深入源码

skiplist结构

template<typename Key, class Comparator>
class SkipList {private://Node结构体声明struct Node;public:// Create a new SkipList object that will use "cmp" for comparing keys,// and will allocate memory using "*arena".  Objects allocated in the arena// must remain allocated for the lifetime of the skiplist object.//构造函数explicit SkipList(Comparator cmp, Arena* arena);// Insert key into the list.// REQUIRES: nothing that compares equal to key is currently in the list.//插入函数声明void Insert(const Key& key);// Returns true iff an entry that compares equal to key is in the list.bool Contains(const Key& key) const;//省略部分为迭代器private:enum { kMaxHeight = 12 };// Immutable after construction//比较器Comparator const compare_;//内存池Arena* const arena_;    // Arena used for allocations of nodes//头部节点Node* const head_;// Modified only by Insert().  Read racily by readers, but stale// values are ok.//整个list的高度port::AtomicPointer max_height_;   // Height of the entire list//get最高的高度inline int GetMaxHeight() const {return static_cast<int>(reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load()));}// Read/written only by Insert().//在Insert中使用Random rnd_;//新建NodeNode* NewNode(const Key& key, int height);//获取随机高度int RandomHeight();//判断是否相等bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }// Return true if key is greater than the data stored in "n"//当前key是否在Node之后bool KeyIsAfterNode(const Key& key, Node* n) const;// Return the earliest node that comes at or after key.// Return nullptr if there is no such node.//// If prev is non-null, fills prev[level] with pointer to previous// node at "level" for every level in [0..max_height_-1].//找与key相等或比key大的节点//prev用于存放每一层中符合条件Node的前一个NodeNode* FindGreaterOrEqual(const Key& key, Node** prev) const;// Return the latest node with a key < key.// Return head_ if there is no such node.Node* FindLessThan(const Key& key) const;// Return the last node in the list.// Return head_ if list is empty.Node* FindLast() const;// No copying allowedSkipList(const SkipList&);void operator=(const SkipList&);
};
构造函数

在理解了跳表的原理,我们可以知道每一个高度的第一个节点总是head节点。

template<typename Key, class Comparator>
SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena): compare_(cmp),arena_(arena),head_(NewNode(0 /* any key will do */, kMaxHeight)),max_height_(reinterpret_cast<void*>(1)),rnd_(0xdeadbeef) {for (int i = 0; i < kMaxHeight; i++) {//给每一个高度上的head_设置next节点为nullptrhead_->SetNext(i, nullptr);}
}

跳表的结构其实比较简单,其内部成员比较重要的部分是整个List的头部Nodehead_,比较器compare_,内存池arena和一个枚举类型,限制了List的最大高度。(在leveldb实现中用的是height这个单词,为了理解方便我用了层次level这个说法,本质上是没有区别的,但是需要注意的是leveldb之所以称之为leveldb并不是因为跳表的结构,而是sstable的结构)

其中还有一个内部类Iterator迭代器,以及声明的一些函数,我将放在下一节讲这部分内容,接下来先了解一下跳表中的Node节点的结构。

Node-跳表的节点


之前提到,跳表是对链表的改进,在很多实现上其实跳表都和链表类似,但是为了实现跳表的独有特性,在一些地方做了改进。
我们的链表通常有单链表,双向循环链表等,都是比较规整的,而跳表可以说是不太规整,在不同的层次(或是高度height)上来看一个节点指向的next节点是不同的,所以这里使用了一个原子指数组来存放这些next节点,数组的下标值就是Node节点在的当前层次。下面来看源码实现:

// Implementation details follow
template<typename Key, class Comparator>
struct SkipList<Key,Comparator>::Node {//构造函数,防止隐式转换explicit Node(const Key& k) : key(k) { }Key const key;// Accessors/mutators for links.  Wrapped in methods so we can// add the appropriate barriers as necessary.//连接的访问器/变异器。用方法包装//这样我们就可以根据需要添加适当的屏障//参数n为指定高度Node* Next(int n) {assert(n >= 0);// Use an 'acquire load' so that we observe a fully initialized// version of the returned Node.return reinterpret_cast<Node*>(next_[n].Acquire_Load());}//设置当前节点的next节点,n指定高度,x为下一个节点void SetNext(int n, Node* x) {assert(n >= 0);// Use a 'release store' so that anybody who reads through this// pointer observes a fully initialized version of the inserted node.next_[n].Release_Store(x);}// No-barrier variants that can be safely used in a few locations.//无内存屏障Node* NoBarrier_Next(int n) {assert(n >= 0);return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());}void NoBarrier_SetNext(int n, Node* x) {assert(n >= 0);next_[n].NoBarrier_Store(x);}private:// Array of length equal to the node height.  next_[0] is lowest level link.//数组的长度等于节点的高度,next_[0]是最底层的link//AtomicPointer:原子指针port::AtomicPointer next_[1];
};

数据库管理系统中事务具有四个特性:ACID原子性,一致性,隔离性和持久性。
原子性是指事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。

port::AtomicPointer 是一个封装类,其作用是保证操作的原子性。其采用了内存屏障(关于内存屏障这里不介绍太多)来实现同步,其内部成员其实是一个void*类型的指针。
代码位置:leveldb-master/port/atomic_pointer.h

class AtomicPointer {private://内部私有成员void* rep_;public:AtomicPointer() { }explicit AtomicPointer(void* p) : rep_(p) {}inline void* NoBarrier_Load() const { return rep_; }inline void NoBarrier_Store(void* v) { rep_ = v; }inline void* Acquire_Load() const {void* result = rep_;MemoryBarrier();return result;}inline void Release_Store(void* v) {//使用内存屏障来保证同步MemoryBarrier();rep_ = v;}
};

总结

在之前的博客中讲到了跳表的原理,本节主要讲了一下跳表的结构。
利用数组的下标值来表示高度是非常巧妙的一种做法,虽然跳表的整体结构可能不像链表那么规整,但是其实并不难理解,使用起来也和链表类似。
比如说访问下一个节点,在一般的链表中我们会用Node->next,在条表中实现了next()方法,我们可以使用Node->next(n)来访问,其中的n表示的是高度。
接下来我们将详细介绍跳表中的一些方法。

levelDB源码阅读-skiplist跳表 上相关推荐

  1. leveldb 源码阅读 一周目完成

    leveldb 源码阅读项目 - github rsy 一些感想 前前后后总共读了2周,国庆一周,最近这一周 第一周读了 util 和 table 里面的东西.util 还算比较好读:table 里面 ...

  2. 数组、链表、LinkedList源码分析、跳表

    一.数组 1.什么是数组 数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据 线性表:数据排成像一条线一样的结构.每个线性表上的数据最多只有前和后两个方向.其实除了数组,链 ...

  3. scrcpy源码阅读及在Ubuntu上的实现(一)——了解原理

    那开篇就问问为什么需要研究这个源码吧: 在移动互联网的时代下,手机的功能是日益增加的,要使工作变得更加的高效,那么键盘鼠标其实是必不可少的.在许多软件的架构中,其实并没有提供对应的桌面版本,也不兼容基 ...

  4. scrcpy源码阅读及在Ubuntu上的实现(三)——使用ZeroMQ传输yuv数据并使用Python订阅

    目录 0x01 什么是ZeroMQ? 0x02 ZeroMQ的消息模型 0x03 回到任务 0x04 封装我们的yuv图像以及发布者 0x05 使用Python订阅ZeroMQ的发布 0x06 需要注 ...

  5. leveldb源码分析:数据插入续(跳表)

    leveldb数据的插入-跳表 本文主要是接着上一篇文章,继续深入探索Write函数调用插入之后的流程. status = WriteBatchInternal::InsertInto(updates ...

  6. React 表单源码阅读笔记

    1 概念 1.1 什么是表单 实际上广义上的表单并不是特别好界定,维基上讲表单是一系列带有空格的文档,用于输写或选择.更具体的,在网页中表单主要负责数据采集的功能,我们下文中所提到的表单都指后者.如下 ...

  7. gh-ost大表DDL工具源码阅读

    gh-ost大表DDL工具源码阅读 最终目的 开发环境与测试数据库准备 一个简单的ddl案例 debug分析程序执行过程 vscode debug配置 变量介绍 核心处理逻辑 分析我的需求 最终目的 ...

  8. 【Dubbo源码阅读系列】之远程服务调用(上)

    今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道 ...

  9. 16 BasicHashTable基本哈希表类(三)——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  10. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

最新文章

  1. python对数组的基本操作_[宜配屋]听图阁
  2. 【数理逻辑】谓词逻辑 ( 个体词 | 个体域 | 谓词 | 全称量词 | 存在量词 | 谓词公式 | 习题 )
  3. B端产品如何进行业务全场景的需求梳理?
  4. Android 软键盘自动弹出和关闭
  5. 前端学习(2780):创建项目和外观
  6. Leetcode--394. 字符串解码(Java)
  7. 邓西百度网盘批量转存检测工具 v1.0.0818
  8. 机器学习- 吴恩达Andrew Ng - week3-1 Classification
  9. 给定一个字符串,求第一个不重复的字符
  10. 统计通话次数和时间的软件_通话时间统计手机下载-通话时间统计手机版下载v2.3-西西软件下载...
  11. 74LS 系列与 74HC,74HCT,CD系列的区别
  12. DIYGW-UI-PHP是一款基于thinkphp framework和 element admin开发而成的前后端分离系统
  13. 安卓和鸿蒙系统,对标Apple Watch!魅族推首款智能手表,官宣接入华为鸿蒙系统...
  14. request库的基本用法
  15. Cauchy–Schwarz inequality理解
  16. double类型问题(精度丢失和自动拆箱)
  17. 深度学习 --- 受限玻尔兹曼机详解(RBM)
  18. 安天移动安全助力银行卡检测中心智能POS安全检测
  19. 高斯混合模型(GMM,GOM)
  20. Spring是如何通过IOC来创建对象的?一文足矣

热门文章

  1. 软件测试面试过程中常见的问题
  2. Toplitz矩阵 Hankel矩阵 Hilbert矩阵
  3. 计算机导论知识梳理,《计算机导论》知识点
  4. 全国省份简称(备用)
  5. 二级计算机题世界动物日,计算机二级考试真题-PPT-张宇-世界动物日介绍
  6. Windows Server 2008 R2 官方简体中文免费企业版/标准版/数据中心版下载
  7. 台式电脑计算机怎么打不开怎么回事,为什么电脑自带的软件打不开怎么办
  8. ArcGIS网络分析
  9. 联想电脑打不开摄像头
  10. python里随机生成属性_Python随机生成信用卡卡号的实现方法