leveldb 学习记录(一) skiplist
leveldb
LevelDb是一个持久化存储的KV系统,并非完全将数据放置于内存中,部分数据也会存储到磁盘上。
想了解这个由谷歌大神编写的经典项目.
可以从数据结构以及数据结构的处理下手,也可以从示例的某一点深入跟进系统,查看处理流程.
windows下编译leveldb 地址 leveldb 源码编译 vs版本
目前手头资料中,源码中的文档以及网络的代码分析心得如下,本文也做了参考,感谢作者.
流程类
结构入手类
1 arena内存池略过。 nginx内存池 stl内存池均可参考实现原理(《stl源码分析》)
2 bloomfilter相当于多重哈希,比对哈希值判断是否有相同元素插入 略过。
3 数据结构skiplist 多数操作log(n)
参考 https://segmentfault.com/a/1190000003051117
跳表的关键点在于定义和查找方法
定义如下:
SkipList的定义:
1. 一个跳表应该有几个层(level)组成;
2. 跳表的第一层包含所有的元素;
3. 每一层都是一个有序的链表;
4. 如果元素x出现在第i层,则所有比i小的层都包含x;
5. 第i层的元素通过一个down指针指向下一层拥有相同值的元素;
6. 在每一层中,-1和1两个元素都出现(分别表示INT_MIN和INT_MAX);
7. Top指针指向最高层的第一个元素。
图示:

find
查找方法为
1 起点为最高层第一个元素 若元素小于查找值 则查找该元素的后继值
2 若元素大于查找值 则降低层级再次查找
3 若该元素的后继值 为NULL或者大于该值 ,则降低层数再次查找
4 直到查找成功或者达到表的最底层且无元素可查找
图示中查找 17
起点为最高层第一个元素 6 6<17 6的下一个元素为null 则在元素6中降低层级
再次查找6的下一个元素 是25 降低层级
再次查找6的下一个元素 是9
再次查找9的下一个元素 是25 降低层级
再次查找9的下一个元素 是12
再次查找12的下一个元素 是19 此处为最低层级 则未查找到(进行插入)
insert
查找也可用于insert 注意insert 元素时候层级是随机的
leveldb 中skiplist 结构如下(内存池与原子指针等结构暂时不予理会)
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.
Node* Next(int n) {
assert(n >= );
// Use an 'acquire load' so that we observe a fully initialized
// version of the returned Node.
return reinterpret_cast<Node*>(next_[n].Acquire_Load());
}
void SetNext(int n, Node* x) {
assert(n >= );
// 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 >= );
return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());
}
void NoBarrier_SetNext(int n, Node* x) {
assert(n >= );
next_[n].NoBarrier_Store(x);
} private:
// Array of length equal to the node height. next_[0] is lowest level link.
port::AtomicPointer next_[];
}; template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node*
SkipList<Key,Comparator>::NewNode(const Key& key, int height) {
char* mem = arena_->AllocateAligned(
sizeof(Node) + sizeof(port::AtomicPointer) * (height - ));
return new (mem) Node(key);
}
抛开内存池和多线程情况下的原子操作 定义很简单
分配 获取下一个元素 设置下一个元素
template<typename Key, class Comparator>中的 key是元素类型 Comparator是元素比较大小的策略
元素插入操作如下
template<typename Key, class Comparator>
void SkipList<Key,Comparator>::Insert(const Key& key) {
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
// here since Insert() is externally synchronized.
Node* prev[kMaxHeight];
Node* x = FindGreaterOrEqual(key, prev); assert(x == NULL || !Equal(key, x->key)); int height = RandomHeight();
if (height > GetMaxHeight()) {
for (int i = GetMaxHeight(); i < height; i++) {
prev[i] = head_;
}
//fprintf(stderr, "Change height from %d to %d\n", max_height_, height); // It is ok to mutate max_height_ without any synchronization
// with concurrent readers. A concurrent reader that observes
// the new value of max_height_ will see either the old value of
// new level pointers from head_ (NULL), or a new value set in
// the loop below. In the former case the reader will
// immediately drop to the next level since NULL sorts after all
// keys. In the latter case the reader will use the new node.
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
} x = NewNode(key, height);
for (int i = ; i < height; i++) {
// NoBarrier_SetNext() suffices since we will add a barrier when
// we publish a pointer to "x" in prev[i].
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
prev[i]->SetNext(i, x);
}
}
插入元素的层级是随机的 并且获取当前最大层级数并未使用原子操作 因为根据逻辑并无影响 不使用原子操作也是性能上的一种考虑
元素读取操作如下
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)
const {
Node* x = head_;
int level = GetMaxHeight() - ;
while (true) {
Node* next = x->Next(level);
if (KeyIsAfterNode(key, next)) {
// Keep searching in this list
x = next;
} else {
if (prev != NULL) prev[level] = x;
if (level == ) {
return next;
} else {
// Switch to next list
level--;
}
}
}
}

find
查找方法为
1 起点为最高层第一个元素 若元素小于查找值 则查找该元素的后继值
2 若元素大于查找值 则降低层级再次查找
3 若该元素的后继值 为NULL或者大于该值 ,则降低层数再次查找
4 直到查找成功或者达到表的最底层且无元素可查找
图示中查找 17
起点为最高层第一个元素 6 6<17 6的下一个元素为null 则在元素6中降低层级
再次查找6的下一个元素 是25 降低层级
再次查找6的下一个元素 是9
再次查找9的下一个元素 是25 降低层级
再次查找9的下一个元素 是12
再次查找12的下一个元素 是19 此处为最低层级 则未查找到(进行插入)
leveldb 学习记录(一) skiplist的更多相关文章
- leveldb 学习记录(四) skiplist补与变长数字
在leveldb 学习记录(一) skiplist 已经将skiplist的插入 查找等操作流程用图示说明 这里在介绍 下skiplist的代码 里面有几个模块 template<typenam ...
- leveldb 学习记录(三) MemTable 与 Immutable Memtable
前文: leveldb 学习记录(一) skiplist leveldb 学习记录(二) Slice 存储格式: leveldb数据在内存中以 Memtable存储(核心结构是skiplist 已介绍 ...
- leveldb 学习记录(四)Log文件
前文记录 leveldb 学习记录(一) skiplistleveldb 学习记录(二) Sliceleveldb 学习记录(三) MemTable 与 Immutable Memtablelevel ...
- leveldb 学习记录(五)SSTable格式介绍
本节主要记录SSTable的结构 为下一步代码阅读打好基础,考虑到已经有大量优秀博客解析透彻 就不再编写了 这里推荐 https://blog.csdn.net/tankles/article/det ...
- leveldb 学习记录(七) SSTable构造
使用TableBuilder构造一个Table struct TableBuilder::Rep { // TableBuilder内部使用的结构,记录当前的一些状态等 Options options ...
- leveldb 学习记录(八) compact
随着运行时间的增加,memtable会慢慢 转化成 sstable. sstable会越来越多 我们就需要进行整合 compact 代码会在写入查询key值 db写入时等多出位置调用MaybeSche ...
- leveldb 学习记录(二) Slice
基本每个KV库都有一个简洁的字符串管理类 比如redis的sds 比如leveldb的slice 管理一个字符串指针和数据长度 通过对字符串指针 长度的管理实现一般的创建 判断是否为空 获取第N个位 ...
- leveldb 学习记录(六)SSTable:Block操作
block结构示意图 sstable中Block 头文件如下: class Block { public: // Initialize the block with the specified con ...
- LevelDB学习笔记 (3): 长文解析memtable、跳表和内存池Arena
LevelDB学习笔记 (3): 长文解析memtable.跳表和内存池Arena 1. MemTable的基本信息 我们前面说过leveldb的所有数据都会先写入memtable中,在leveldb ...
随机推荐
- PTA上Java问题自查与提问方法
自查 首先请一定先看这篇文章<PTA中提交Java程序的一些套路>.该文囊括了PTA中提交Java程序的几乎所有常见问题,请仔细阅读可以少踩很多坑 题目测试方法:复制样例输入,然后打开一个 ...
- 【剑指offer】求一组数据中最小的K个数
题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. *知识点:Java PriorityQueue 调整新插入元素 转自h ...
- ps-如何去水印
现在,版权意识越来越明显了,所以加水印的图片越来越多了,但我们在一些特定的情况又不得不去使用那些图片,去水印又是问题.今天,我来说下如何去水印. 一.ps-仿制图章工具去水印 1.打开ps,打开待去水 ...
- 学习MeteoInfo二次开发教程(七)
1.站点文件 12010615.syn在D:\Program Files (x86)\MeteoInfo\SampleSYNOP_Stations.csv在D:\Program Files (x86) ...
- Twisted网络库编程实例
于这一周看了python的第三方网络库Twisted,英文看的头比较大,想看英文的话点击这里.如果英文很烂,可以看中文,这里.总的来说我了解到的主要包括以下三个东东:Factory.protocol和 ...
- python学习笔记_week26
note 一.CMDB -采集资产 -API -后台管理 -资产列表(CURD) -业务线列表(CURD) -用户列表(CURD) -组列表(CURD) ... ===>简单<=== 公共 ...
- 如何使用 Visual C# .NET 处理 Excel 事件
事件处理概述 Visual C# .NET 使用委派处理来自组件对象模型 (COM) 服务器的事件.委派是 Microsoft Visual Studio .NET 中的一个新概念.对于 COM 事件 ...
- hitTest,UIWindow sendEvent ,touchbegan, 响应链
https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_th ...
- UNITY 打包时提示sdk tools 或 sdk build tools版本低时可以直接点update 按钮进行更新
如题.如果不更新,而选择 : use newest version installed ,打包到结尾时可能会报错,莫名其妙的java错误 而且,SDK一旦被更新后,其所在目录的 SDK MANAGER ...
- GDI+_Png图片浏览器
'昨天看见有人问VB6支不支持PNG,刚好我正在研究GDI+,于是做了这个演示程序.'演示下载地址:百度网盘|'下面为设计界面和运行效果截图 ' 千万别喷界面丑. /无奈 .