LevelDB源码分析-Write
Write
LevelDB提供了write和put两个接口进行插入操作,但是put实际上是调用write实现的,所以我在这里只分析write函数:
Status DBImpl::Write(const WriteOptions &options, WriteBatch *my_batch)
首先初始化一个Writer对象,Writer对象用于封装一个插入操作,LevelDB用一个deque来管理Writer对象,新建的Writer对象被插入到这个deque的尾部,如果Writer对象未被处理且不在deque头部,则会一直等待:
Writer w(&mutex_);
w.batch = my_batch;
w.sync = options.sync;
w.done = false;
MutexLock l(&mutex_);
writers_.push_back(&w);
while (!w.done && &w != writers_.front())
{
w.cv.Wait();
}
if (w.done)
{
return w.status;
}
然后调用MakeRoomForWrite函数保证memtable中有插入的空间:
// May temporarily unlock and wait.
Status status = MakeRoomForWrite(my_batch == nullptr);
uint64_t last_sequence = versions_->LastSequence();
Writer *last_writer = &w;
接下来调用BuildBatchGroup函数将此时writers_队列中的Writer对象全部封装为一个WriteBatch,也就是说LevelDB实际上一次会处理当前的所有插入任务:
if (status.ok() && my_batch != nullptr)
{ // nullptr batch is for compactions
WriteBatch *updates = BuildBatchGroup(&last_writer);
WriteBatchInternal::SetSequence(updates, last_sequence + 1);
last_sequence += WriteBatchInternal::Count(updates);
再调用函数将KV值插入memtable中:
// Add to log and apply to memtable. We can release the lock
// during this phase since &w is currently responsible for logging
// and protects against concurrent loggers and concurrent writes
// into mem_.
{
mutex_.Unlock();
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())
{
status = WriteBatchInternal::InsertInto(updates, mem_);
}
mutex_.Lock();
if (sync_error)
{
// The state of the log file is indeterminate: the log record we
// just added may or may not show up when the DB is re-opened.
// So we force the DB into a mode where all future writes fail.
RecordBackgroundError(status);
}
}
if (updates == tmp_batch_)
tmp_batch_->Clear();
versions_->SetLastSequence(last_sequence);
}
将队列中此次已经处理的Writer对象都删除,并且给那些Writer对象发送信号,使它们能够结束自己的任务:
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;
}
如果当前队列中有新的Writer对象,发送信号激活队首的Writer对象:
// Notify new head of write queue
if (!writers_.empty())
{
writers_.front()->cv.Signal();
}
return status;
Write函数调用的MakeRoomForWrite函数为:
// REQUIRES: mutex_ is held
// REQUIRES: this thread is currently at the front of the writer queue
Status DBImpl::MakeRoomForWrite(bool force)
函数将一直进行循环,判断各个条件并执行相应操作,直到memtable中有足够空间可以插入。
如果level0的文件数量超过阈值,且这是第一次检测到这种情况,那么sleep1ms:
else if (
allow_delay &&
versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger)
{
// We are getting close to hitting a hard limit on the number of
// L0 files. Rather than delaying a single write by several
// seconds when we hit the hard limit, start delaying each
// individual write by 1ms to reduce latency variance. Also,
// this delay hands over some CPU to the compaction thread in
// case it is sharing the same core as the writer.
mutex_.Unlock();
env_->SleepForMicroseconds(1000);
allow_delay = false; // Do not delay a single write more than once
mutex_.Lock();
}
如果当前memtable中有足够的空间,则跳出循环:
else if (!force &&
(mem_->ApproximateMemoryUsage() <= options_.write_buffer_size))
{
// There is room in current memtable
break;
}
如果当前memtable中空间不足,immutable memtable也没有被写出,则等待compact的背景线程完成compact(immutable memtable需要compact):
else if (imm_ != nullptr)
{
// We have filled up the current memtable, but the previous
// one is still being compacted, so we wait.
Log(options_.info_log, "Current memtable full; waiting...\n");
background_work_finished_signal_.Wait();
}
如果当前memtable空间不足,level0中的文件数量超过了阈值,且不是第一次检测到这种情况,则等待compact的背景线程完成compact(level0需要compact):
else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger)
{
// There are too many level-0 files.
Log(options_.info_log, "Too many L0 files; waiting...\n");
background_work_finished_signal_.Wait();
}
如果以上情况都不存在,则说明可以将当前memtable写入immutable memtable,然后创建一个新的memtable,当然需要调用MaybeScheduleCompaction函数,因为产生了immutable memtable需要compact:
else
{
// Attempt to switch to a new memtable and trigger compaction of old
assert(versions_->PrevLogNumber() == 0);
uint64_t new_log_number = versions_->NewFileNumber();
WritableFile *lfile = nullptr;
s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
if (!s.ok())
{
// Avoid chewing through file number space in a tight loop.
versions_->ReuseFileNumber(new_log_number);
break;
}
delete log_;
delete logfile_;
logfile_ = lfile;
logfile_number_ = new_log_number;
log_ = new log::Writer(lfile);
imm_ = mem_;
has_imm_.Release_Store(imm_);
mem_ = new MemTable(internal_comparator_);
mem_->Ref();
force = false; // Do not force another compaction if have room
MaybeScheduleCompaction();
}
231 Love u
LevelDB源码分析-Write的更多相关文章
- leveldb源码分析--SSTable之block
在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...
- leveldb源码分析--WriteBatch
从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...
- leveldb源码分析--Key结构
[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...
- Leveldb源码分析--1
coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...
- leveldb源码分析--日志
我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...
- leveldb源码分析之Slice
转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...
- LevelDB源码分析--Cache及Get查找流程
本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...
- leveldb源码分析--SSTable之TableBuilder
上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...
- leveldb源码分析之内存池Arena
转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...
- 【转】Leveldb源码分析——1
先来看看Leveldb的基本框架,几大关键组件,如图1-1所示. Leveldb是一种基于operation log的文件系统,是Log-Structured-Merge Tree的典型实现.LSM源 ...
随机推荐
- 树莓派3 Raspberry系统安装samba
默认Raspberry不自带samb,需要手动安装. 如果默认的rasp源不好用的话,可以使用下面从网上找的: deb http://mirrors.cqu.edu.cn/Raspbian/raspb ...
- ORACLE数据库在导入导出时序列不一致的问题
ORACLE数据库在导入导出时序列不一致的问题 在使用ORACLE数据库时,当给一个表设置自增字段时,我们经常会使用到序列+触发器来完成.但当你需要对数据库进行导入导出时,序列很容易出问题. 当你 ...
- for XML path 转义
select (SELECT 'ab<&c' FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)')
- 黄聪:微信h5支付demo微信H5支付demo非微信浏览器支付demo微信wap支付
一.首先先确定H5支付权限已经申请! 二.开发流程 1.用户在商户侧完成下单,使用微信支付进行支付 2.由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB ...
- ubuntu设置 SSH 通过密钥对登录
1. 制作密钥对 首先在服务器上制作密钥对.登录到打算使用密钥登录的账户,然后执行以下命令: [root@host ~]$ ssh-keygen <== 建立密钥对 Generating pub ...
- 【CentOS】PostgreSQL安装与设定
本教程适合Centos6.7或者RedHat. PostgreSQL安装 1.Postgresql安装包确认 yum list postgresql* postgresql-server.x86_64 ...
- 使用Netty开发RPC的技术原理
本片文字摘抄自https://www.cnblogs.com/jietang/p/5615681.html 1.定义RPC请求消息.应答消息结构,里面要包括RPC的接口定义模块,包括远程调用的类名.方 ...
- Laya for H5 之Bug追踪
Laya For H5之Bug追踪 H5游戏一旦上线后,如何跟踪用户的崩溃日志呢?现在有很多第三方的工具,比如fundebug,其sdk接入简单,只需寥寥几行代码就可以追踪h5游戏的崩溃日志,bug日 ...
- 隐马尔可夫模型(HMM) 学习笔记
在中文标注时,除了条件随机场(crf),被提到次数挺多的还有隐马尔可夫(HMM),通过对<统计学习方法>一书的学习,我对HMM的理解进一步加深了. 第一部分 介绍隐马尔可夫 隐马尔可夫模型 ...
- LeetCode【111. 二叉树的最小深度】
最小深度,看起来很简单,就是左右节点的深度最小值 定义一个函数,计算其深度 class Solution { public int minDepth(TreeNode root) { if(root ...