leveldb源码分析--插入删除流程
由于网络上对leveldb的分析文章都比较丰富,一些基础概念和模型都介绍得比较多,所以本人就不再对这些概念以专门的篇幅进行介绍,本文主要以代码流程注释的方式。
首先我们从db的插入和删除开始以对整个体系有一个感性的认识,首先看插入:
Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {
WriteBatch batch; //leveldb中不管单个插入还是多个插入都是以WriteBatch的方式进行的
batch.Put(key, value);
return Write(opt, &batch);
}
Delete也类似,只是调用了WriteBatch 的 Delete(key), 这样再内部会以不同的形式编码传递至下一步进行处理。具体的WriteBatch的实现和编码方式在稍后的文章中进行介绍。Delete和Put都调用了Write,,这里的Write是在DBImpl::Write中通过虚函数的形式实现对其调用的,我们接着看Write的流程
Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) {
Writer w(&mutex_);
w.batch = my_batch;
w.sync = options.sync;
w.done = false;
/*
产生一个Writer对象,然后保存必要的锁、batch、和同步写的相关信息
*/
MutexLock l(&mutex_);
writers_.push_back(&w); // 上锁,然后放入待写的队列中
while (!w.done && &w != writers_.front()) {
/* 这里设计比较特别,需要跟后面的 BuildBatchGroup结合起来看,这里大致的意思是
一直等待到这次写完成或者这次写被放在队列的最前面,BuildBatchGroup会将队列
里所有sync设置相同的写请求组成一个WriteBatch进行写入,这里的写请求有可能在
别的线程完成写操作了,而是否在队列首的判断是有可能此刻没有其他线程在写循环中,
或者本次写请求和前面的写请求的同步设置不一致,那么这种情况就需要自己进入该线
程完成写的操作。
*/
w.cv.Wait();
}
if (w.done) {
return w.status;
} // 这个函数的主要作用是清理内存表和外存(磁盘)的表使内存表腾出空间插入新的数据
// 这里的设计比较复杂设计到leveldb 的很多核心设计,我们这里先大致了解其功能
Status status = MakeRoomForWrite(my_batch == NULL);
uint64_t last_sequence = versions_->LastSequence();
Writer* last_writer = &w;
if (status.ok() && my_batch != NULL) { // NULL batch is for compactions
WriteBatch* updates = BuildBatchGroup(&last_writer);
WriteBatchInternal::SetSequence(updates, last_sequence + );
last_sequence += WriteBatchInternal::Count(updates); {
mutex_.Unlock();BuildBatchGroup
// 这里讲组装好的batch内容写入log,并根据同步设置判断是否同步到磁盘
status = log_->AddRecord(WriteBatchInternal::Contents(updates));
bool sync_error = false;
if (status.ok() && options.sync) {
status = logfile_->Sync();
if (!status.ok()) {
sync_error = true;
}
}
if (status.ok()) {
// 写入内存表,这里采用了一个遍历WriteBatch完成插入的方式,稍后分析
status = WriteBatchInternal::InsertInto(updates, mem_);
}
mutex_.Lock();
if (sync_error) {
// 如果同步错误则记录相应错误信息.
RecordBackgroundError(status);
}
}
// 删除在BuildBatch里面设置的零时Batch的内容
if (updates == tmp_batch_) tmp_batch_->Clear();
versions_->SetLastSequence(last_sequence);
}
while (true) {
// 唤醒所有等待写入的线程
Writer* ready = writers_.front();
writers_.pop_front();
if (ready != &w) {
ready->status = status;
ready->done = true;
ready->cv.Signal();
}
if (ready == last_writer) break;
} // Notify new head of write queue,因为可能请求时不在队首而进入了等待状态,
// 这样唤醒他使其成为新的队首写线程,进行MakeRoomForWrite等一系列操作
if (!writers_.empty()) {
writers_.front()->cv.Signal();
} return status;
}
所以从流程可以清晰的看到插入删除的流程主要为:
1. 将这条KV记录以顺序写的方式追加到log文件末尾;
2. 将这条KV记录插入内存中的Memtable中,在插入过程中如果刚好后台进程在compaction会短暂停顿以为后台进程compaction腾出时间及cpu
这里涉及到一次磁盘读写操作和内存SkipList的插入操作,但是这里的磁盘写时文件的顺序追加写入效率是很高的,所以并不会导致写入速度的降低;
而且从流程分析我们知道,在插入(删除)过程中如果多线程同时进行,那么这些操作将会将操作的同步设置相同的相邻的操作合并为一个批插入,这样可以使整个系统的总吞吐量更大。所以一次插入记录操作只会等待一次磁盘文件追加写和内存SkipList插入操作,这是为何leveldb写入速度如此高效的根本原因。
我们这里讲插入和删除以等同的方式进行了介绍,可能有的朋友会觉得奇怪,删除不是需要查找到插入的原始记录的么?而leveldb进行了一个巧妙的将随机读写,转换为顺序读写的方式,那就是其并不存在立即删除的操作,而是与插入操作相同将插入操作插入的是Key:Value值改为删除操作插入的是“Key:删除标记”,并不真正立即去删除记录,而是后台Compaction的时候才去做对应的真正的删除操作,这又极大的提高了leveldb的效率。
leveldb源码分析--插入删除流程的更多相关文章
- leveldb源码分析--WriteBatch
从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...
- leveldb源码分析--Key结构
[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...
- leveldb源码分析--SSTable之block
在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...
- Leveldb源码分析--1
coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...
- nodejs的Express框架源码分析、工作流程分析
nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...
- openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)
这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的f ...
- Okhttp源码分析--基本使用流程分析
Okhttp源码分析--基本使用流程分析 一. 使用 同步请求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Re ...
- SpringMVC源码分析-400异常处理流程及解决方法
本文涉及SpringMVC异常处理体系源码分析,SpringMVC异常处理相关类的设计模式,实际工作中异常处理的实践. 问题场景 假设我们的SpringMVC应用中有如下控制器: 代码示例-1 @Re ...
- Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程
一.序列化类的增.删.改.查 用drf的序列化组件 -定义一个类继承class BookSerializer(serializers.Serializer): -写字段,如果不指定source ...
随机推荐
- [转] TCPIP 网络协议层对应的RFC文档
TCPIP网络协议层对应的RFC文档 RFC - Request For Comments 请求注解 TCP/IP层 网络协议 RFC文档 Physical Layer Data Link Layer ...
- AndroidStudio生成自己的Compile依赖
在AndroidStudio中经常使用compile的方式来导入第三方代码,如图所示: 想要拥有自己的依赖,可参考本篇的方法 在github上托管一个带有Android Libray的项目,我们要co ...
- jQuery操纵cookie(原生javascript处理cookie)
jQuery也是可以操作cookie的 1.首先下载jQuery.js 以及 jquery.cookie.js 这两个文件 2.安装(其实就是引用) <html> <he ...
- C语言中的条件编译
通常情况,我们想让程序选择性地执行,多会使用分支语句,比如if-else 或者switch-case 等.但有些时候,可能在程序的运行过程中,某个分支根本不会执行. 比如我们要写一个跨平台项目,要求项 ...
- Koa框架教程
Node主要用在开发 Web 应用.这决定了使用 Node,往往离不开 Web 应用框架. Koa 就是一种简单好用的 Web 框架.它的特点是优雅.简洁.表达力强.自由度高.本身代码只有1000多行 ...
- Linux的用户(组),权限,文件精妙的三角关系,和强大的帮助系统
在linux中一切都是文件(文件夹和硬件外设是特殊的文件),如果有可能尽量使用文本文件.文本文件是人和机器能理解的文件,也成为人和机器进行 交流的最好途径.由于所有的配置文件都是文本,所以你只需要一个 ...
- CentOS7.2配置Hadoop2.6.5
Hadoop配置文件 /etc/profile 配置Java和Hadoop环境 export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export CLAS ...
- XRP节点部署
目录 XRP节点部署 准备 硬软件配置(建议) 安装Rippled服务 一. 以Stock Server模型运行 在何种情况下运行此模式 二 .以 Validator模式运行 在何种情况下运行此模式 ...
- 监测代码执行时间之Stopwatch
var sw = new System.Diagnostics.Stopwatch(); sw.Start(); 中间为要执行的代码 sw.Stop(); var msg = "上面操作耗时 ...
- secureCRT的文件上传技巧
现在我们经常会习惯性的使用windows系统,但现在开发项目和维护中经常都在使用linux服务器,以为它的性能更强.更精简. 学习大数据的同志们和维护后端的同志们,推荐一下secureCRT软件,用起 ...