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的更多相关文章

  1. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  2. leveldb源码分析--WriteBatch

    从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...

  3. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  4. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  5. leveldb源码分析--日志

    我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...

  6. leveldb源码分析之Slice

    转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...

  7. LevelDB源码分析--Cache及Get查找流程

    本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...

  8. leveldb源码分析--SSTable之TableBuilder

    上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...

  9. leveldb源码分析之内存池Arena

    转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...

  10. 【转】Leveldb源码分析——1

    先来看看Leveldb的基本框架,几大关键组件,如图1-1所示. Leveldb是一种基于operation log的文件系统,是Log-Structured-Merge Tree的典型实现.LSM源 ...

随机推荐

  1. 一个极其简易版的vue.js实现

    前言 之前项目中一直在用vue,也边做边学摸滚打爬了近一年.对一些基础原理性的东西有过了解,但是不深入,例如面试经常问的vue的响应式原理,可能大多数人都能答出来Object.defineProper ...

  2. 【CentOS】设置定时执行任务

    1.Crond服务启动状态确认 service crond status crond (pid ) を実行中... 2.追加新的执行任务 crontab -e #Ansible: dnsmasq fo ...

  3. LeetCode——295. Find Median from Data Stream

    一.题目链接: https://leetcode.com/problems/find-median-from-data-stream 二.题目大意: 给定一段数据流,要求求出数据流中的中位数,其中数据 ...

  4. global的使用

    对于一个全局变量,你的函数里如果只使用到了它的值,而没有对其赋值(指a = XXX这种写法)的话,就不需要声明global. 相反,如果你对其赋了值的话,那么你就需要声明global.声明global ...

  5. P1041 传染病控制(dfs)

    P1041 传染病控制 题目背景 近来,一种新的传染病肆虐全球.蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延.不幸的是,由于人们尚未完全认识这种传染 ...

  6. idea中使用MyBatis Generator

    1.新建maven项目 2.新建Generator配置文件 generator_config.xml <?xml version="1.0" encoding="U ...

  7. atom插件

    1.Sync Settings 搭配github,同步你的atom插件信息,配置信息,让你轻松实现一台电脑配置,多台电脑共享. 2.Emmet 能够基于Emmet语法产生HTML,做过前段开发的不会不 ...

  8. 利用CNN神经网络实现手写数字mnist分类

    题目: 1)In the first step, apply the Convolution Neural Network method to perform the training on one ...

  9. spark2.1源码分析1:Win10下IDEA源码阅读环境的搭建

    环境:win10.IDEA2016.3.maven3.3.9.git.scala 2.11.8.java1.8.0_101.sbt0.13.12 下载: #git bash中执行: git clone ...

  10. Python3.6.2安装pip install paramike模块报错

    问题描述: 在有几台电脑上pip install paramike报错 报错内容: Could not find a version that satisfies the requirement sq ...