Open

LevelDB的初始化主要由Open函数实现:

Status DB::Open(const Options &options, const std::string &dbname,
                DB **dbptr)

首先,Open函数调用Recover函数将LevelDB的历史状态恢复:

    *dbptr = nullptr;

    DBImpl *impl = new DBImpl(options, dbname);
    impl->mutex_.Lock();
    VersionEdit edit;
    // Recover handles create_if_missing, error_if_exists
    bool save_manifest = false;
    Status s = impl->Recover(&edit, &save_manifest);

如果恢复后LevelDB当前的memtable为空则创建一个memtable和相应的log文件:

    if (s.ok() && impl->mem_ == nullptr)
    {
        // Create new log and a corresponding memtable.
        uint64_t new_log_number = impl->versions_->NewFileNumber();
        WritableFile *lfile;
        s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
                                         &lfile);
        if (s.ok())
        {
            edit.SetLogNumber(new_log_number);
            impl->logfile_ = lfile;
            impl->logfile_number_ = new_log_number;
            impl->log_ = new log::Writer(lfile);
            impl->mem_ = new MemTable(impl->internal_comparator_);
            impl->mem_->Ref();
        }
    }

如果需要将新建的manifest文件保存下来,就调用LogAndApply函数将当前的VersionEdit对象应用,并在

    if (s.ok() && save_manifest)
    {
        edit.SetPrevLogNumber(0); // No older logs needed after recovery.
        edit.SetLogNumber(impl->logfile_number_);
        s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
    }

最后删除旧文件并尝试进行compaction:

    if (s.ok())
    {
        impl->DeleteObsoleteFiles();
        impl->MaybeScheduleCompaction();
    }
    impl->mutex_.Unlock();
    if (s.ok())
    {
        assert(impl->mem_ != nullptr);
        *dbptr = impl;
    }
    else
    {
        delete impl;
    }
    return s;

Recover函数用于恢复LevelDB,包括恢复VersionSet和memtable:

Status DBImpl::Recover(VersionEdit *edit, bool *save_manifest)

创建存储LevelDB数据的目录(如果出现错误则会被忽略,可能已经存在):

    mutex_.AssertHeld();

    // Ignore error from CreateDir since the creation of the DB is
    // committed only when the descriptor is created, and this directory
    // may already exist from a previous failed creation attempt.
    env_->CreateDir(dbname_);
    assert(db_lock_ == nullptr);
    Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);
    if (!s.ok())
    {
        return s;
    }

如果CURRENT文件不存在(当前LevelDB为新创建时),调用NewDB函数创建LevelDB所需的相关文件(manifest文件和CURRENT文件):

    if (!env_->FileExists(CurrentFileName(dbname_)))
    {
        if (options_.create_if_missing)
        {
            s = NewDB();
            if (!s.ok())
            {
                return s;
            }
        }
        else
        {
            return Status::InvalidArgument(
                dbname_, "does not exist (create_if_missing is false)");
        }
    }
    else
    {
        if (options_.error_if_exists)
        {
            return Status::InvalidArgument(
                dbname_, "exists (error_if_exists is true)");
        }
    }

调用versions_->Recover函数根据manifest文件恢复VersionSet:

    s = versions_->Recover(save_manifest);
    if (!s.ok())
    {
        return s;
    }
    SequenceNumber max_sequence(0);

获取之前尚未处理的log文件:

    // Recover from all newer log files than the ones named in the
    // descriptor (new log files may have been added by the previous
    // incarnation without registering them in the descriptor).
    //
    // Note that PrevLogNumber() is no longer used, but we pay
    // attention to it in case we are recovering a database
    // produced by an older version of leveldb.
    const uint64_t min_log = versions_->LogNumber();
    const uint64_t prev_log = versions_->PrevLogNumber();
    std::vector<std::string> filenames;
    s = env_->GetChildren(dbname_, &filenames);
    if (!s.ok())
    {
        return s;
    }
    std::set<uint64_t> expected;
    versions_->AddLiveFiles(&expected);
    uint64_t number;
    FileType type;
    std::vector<uint64_t> logs;
    for (size_t i = 0; i < filenames.size(); i++)
    {
        if (ParseFileName(filenames[i], &number, &type))
        {
            expected.erase(number);
            if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
                logs.push_back(number);
        }
    }
    if (!expected.empty())
    {
        char buf[50];
        snprintf(buf, sizeof(buf), "%d missing files; e.g.",
                 static_cast<int>(expected.size()));
        return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
    }

通过调用RecoverLogFile函数,按顺序根据这些log文件恢复LevelDB的memtable:

    // Recover in the order in which the logs were generated
    std::sort(logs.begin(), logs.end());
    for (size_t i = 0; i < logs.size(); i++)
    {
        s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit,
                           &max_sequence);
        if (!s.ok())
        {
            return s;
        }

        // The previous incarnation may not have written any MANIFEST
        // records after allocating this log number.  So we manually
        // update the file number allocation counter in VersionSet.
        versions_->MarkFileNumberUsed(logs[i]);
    }

    if (versions_->LastSequence() < max_sequence)
    {
        versions_->SetLastSequence(max_sequence);
    }

    return Status::OK();

RecoverLogFile函数用于根据log文件恢复memtable:

Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log,
                              bool *save_manifest, VersionEdit *edit,
                              SequenceNumber *max_sequence)

首先打开log文件:

    // Open the log file
    std::string fname = LogFileName(dbname_, log_number);
    SequentialFile *file;
    Status status = env_->NewSequentialFile(fname, &file);
    if (!status.ok())
    {
        MaybeIgnoreError(&status);
        return status;
    }

接下来创建一个LogReporter对象用于读取log文件:

    // Create the log reader.
    LogReporter reporter;
    reporter.env = env_;
    reporter.info_log = options_.info_log;
    reporter.fname = fname.c_str();
    reporter.status = (options_.paranoid_checks ? &status : nullptr);
    // We intentionally make log::Reader do checksumming even if
    // paranoid_checks==false so that corruptions cause entire commits
    // to be skipped instead of propagating bad information (like overly
    // large sequence numbers).
    log::Reader reader(file, &reporter, true /*checksum*/,
                       0 /*initial_offset*/);
    Log(options_.info_log, "Recovering log #%llu",
        (unsigned long long)log_number);

然后读取log文件中的日志记录,将KV值插入memtable中,如果memtable的大小超过设定的阈值,就将其compact到level0:

    // Read all the records and add to a memtable
    std::string scratch;
    Slice record;
    WriteBatch batch;
    int compactions = 0;
    MemTable *mem = nullptr;
    while (reader.ReadRecord(&record, &scratch) &&
           status.ok())
    {
        if (record.size() < 12)
        {
            reporter.Corruption(
                record.size(), Status::Corruption("log record too small"));
            continue;
        }
        WriteBatchInternal::SetContents(&batch, record);

        if (mem == nullptr)
        {
            mem = new MemTable(internal_comparator_);
            mem->Ref();
        }
        status = WriteBatchInternal::InsertInto(&batch, mem);
        MaybeIgnoreError(&status);
        if (!status.ok())
        {
            break;
        }
        const SequenceNumber last_seq =
            WriteBatchInternal::Sequence(&batch) +
            WriteBatchInternal::Count(&batch) - 1;
        if (last_seq > *max_sequence)
        {
            *max_sequence = last_seq;
        }

        if (mem->ApproximateMemoryUsage() > options_.write_buffer_size)
        {
            compactions++;
            *save_manifest = true;
            status = WriteLevel0Table(mem, edit, nullptr);
            mem->Unref();
            mem = nullptr;
            if (!status.ok())
            {
                // Reflect errors immediately so that conditions like full
                // file-systems cause the DB::Open() to fail.
                break;
            }
        }
    }

    delete file;

接着判断这个log文件是否可以继续使用,如果可以继续使用当前log文件,那么如果当前memtable不为空,则这个memtable也可以继续使用:

    // See if we should keep reusing the last log file.
    if (status.ok() && options_.reuse_logs && last_log && compactions == 0)
    {
        assert(logfile_ == nullptr);
        assert(log_ == nullptr);
        assert(mem_ == nullptr);
        uint64_t lfile_size;
        if (env_->GetFileSize(fname, &lfile_size).ok() &&
            env_->NewAppendableFile(fname, &logfile_).ok())
        {
            Log(options_.info_log, "Reusing old log %s \n", fname.c_str());
            log_ = new log::Writer(logfile_, lfile_size);
            logfile_number_ = log_number;
            if (mem != nullptr)
            {
                mem_ = mem;
                mem = nullptr;
            }
            else
            {
                // mem can be nullptr if lognum exists but was empty.
                mem_ = new MemTable(internal_comparator_);
                mem_->Ref();
            }
        }
    }

最后如果这个log文件不能继续使用,那么将当前的memtable进行compact写入level0:

    if (mem != nullptr)
    {
        // mem did not get reused; compact it.
        if (status.ok())
        {
            *save_manifest = true;
            status = WriteLevel0Table(mem, edit, nullptr);
        }
        mem->Unref();
    }

    return status;

LevelDB源码分析-Open的更多相关文章

  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. 火狐firebug和firepath插件安装方法(最新)

    火狐浏览器最近下掉了firebug和firepath插件,用户即使下载了火狐55以下的版本,也无法查找到这两个插件. 因此,可以用以下方法来获取这两个插件. 1.下载火狐55以内版本安装包,安装时迅速 ...

  2. KiCad 一款强大的 BOM 和 装配图生成插件

    KiCad 一款强大的 BOM 和 装配图生成插件 可以生成 BOM 和在线的图形. https://github.com/openscopeproject/InteractiveHtmlBom In ...

  3. spring 普通类注入为null,通过自定义SpringUtils解决

    package com.jathams.spring; import org.springframework.beans.BeansException; import org.springframew ...

  4. 浏览器输入url回车后,会发生什么?

    通常我们想访问某一个网址,我们会在浏览器中输入它的域名,然后点击回车进行跳转,这样就可以进到网站的主页,看似简单的两步,其实背后都需要大量的代码在运行,支持,才能完成!那浏览器到底都做了哪些事呢? 一 ...

  5. SqlServer :实现树形结构递归查询(无限极分类)

    SQL Server 2005开始,我们可以直接通过CTE来支持递归查询,CTE即公用表表达式 公用表表达式(CTE),是一个在查询中定义的临时命名结果集将在from子句中使用它.每个CTE仅被定义一 ...

  6. Unity 3D入门简介

    最近在刚开始学习Unity 3D,在这里记录一下学习心得和学习笔记,边学边写,可能会比较零散.好了,废话不多说,今天从Unity 3D入门写起,主要简要介绍一下Unity 3D的和一些学习资料.以下如 ...

  7. JRockit检测Tomcat内存溢出JAVA内存泄漏问题

    http://blog.csdn.net/liyanhui1001/article/details/8240473 公司的一个Java应用系统上线以来,基本每1天OutOfMemoryError: P ...

  8. python基础知识3---字符编码

    阅读目录 一 了解字符编码的知识储备 二 字符编码介绍 三 字符编码应用之文件编辑器 3.1 文本编辑器之nodpad++ 3.2 文本编辑器之pycharm 3.3 文本编辑器之python解释器 ...

  9. java 通信

    UDP package com.le; import org.junit.Test; import java.io.IOException; import java.net.DatagramPacke ...

  10. 解决Table不继承父节点的属性的方法

    解决Table不继承父节点的属性的方法 发现table不继承父节点的属性. 解决方法:给html文件加上<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...