[LevelDB] 写批处理过程详解

leveldb的write代码初看瞎搞一堆,细看则实为短小精悍。

1 Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) {
 2  // -----A begin-------
 3   Writer w(&mutex_);
 4   w.batch = my_batch;
 5   w.sync = options.sync;
 6   w.done = false;
 7   // -----A end  --------
 8
 9
10   // -----B begin-------
11   MutexLock l(&mutex_);
12   writers_.push_back(&w);
13   while (!w.done && &w != writers_.front()) {
14     w.cv.Wait();
15   }
16   if (w.done) {
17     return w.status;
18   }
19   // -----B end  -------
20
21   // May temporarily unlock and wait.
22   Status status = MakeRoomForWrite(my_batch == NULL);
23   uint64_t last_sequence = versions_->LastSequence();
24   Writer* last_writer = &w;
25   if (status.ok() && my_batch != NULL) {  // NULL batch is for compactions
26     WriteBatch* updates = BuildBatchGroup(&last_writer);
27     WriteBatchInternal::SetSequence(updates, last_sequence + 1);
28     last_sequence += WriteBatchInternal::Count(updates);
29
30     // Add to log and apply to memtable.  We can release the lock
31     // during this phase since &w is currently responsible for logging
32     // and protects against concurrent loggers and concurrent writes
33     // into mem_.
34     {
35       // -----C begin-------
36       mutex_.Unlock();
37       // -----C end  -------
38       status = log_->AddRecord(WriteBatchInternal::Contents(updates));
39       if (status.ok() && options.sync) {
40         status = logfile_->Sync();
41       }
42       if (status.ok()) {
43         status = WriteBatchInternal::InsertInto(updates, mem_);
44       }
45       // -----D begin-------
46       mutex_.Lock();
47       // -----D end  -------
48     }
49     if (updates == tmp_batch_) tmp_batch_->Clear();
50
51     versions_->SetLastSequence(last_sequence);
52   }
53
54   // -----E begin-------
55   while (true) {
56     Writer* ready = writers_.front();
57     writers_.pop_front();
58     if (ready != &w) {
59       ready->status = status;
60       ready->done = true;
61       ready->cv.Signal();
62     }
63     if (ready == last_writer) break;
64   }
65   // -----E end -------
66
67
68   // -----F begin-------
69   // Notify new head of write queue
70   if (!writers_.empty()) {
71     writers_.front()->cv.Signal();
72   }
73   // -----F end-------
74
75   return status;
76 }

   如上,A段代码定义一个Writer w, w的成员包括了batch信息,同时初始化了一个条件变量成员(port::CondVar)

  假设同时有w1, w2, w3, w4, w5, w6 并发请求写入。

  B段代码让竞争到mutex资源的w1获取了锁。添加到writers队列里去,此时队列只有一个w1, 从而其顺利的进行BuildBatchGroup。当运行到c段代码时,mutex互斥锁释放,这时(w2, w3, w4, w5, w6)会竞争锁,由于B段代码中不满足队首条件,均等待并释放锁了。从而队列可能会如(w3, w5, w2, w4).

  继而w1进行log写入和memtable写入,之所以这里在无锁状况下时安全的,因为其它的写操作都不满足队首条件,进而不会进入log和memtable写入阶段。 当w1完成log和memtable写入后,进入d段代码,则mutex又锁住,这时B段代码中队列因为获取不到锁则队列不会修改。

  进入E段代码后,w1被pop出来,由于reader==w, 并且ready==last_writer,所以直接到F段代码,唤醒了此时处于队首的w3.

  w3唤醒时,发现自己是队首,可以顺利的进行进入BuildBatchGroup,在该函数中,遍历了目前所有的队列元素,形成一个update的batch,即将w3, w5, w2, w4合并为一个batch. 并将last_writer置为此时处于队尾的最后一个元素w4,c段代码运行后,因为释放了锁资源,队列可能随着DBImpl::Write的调用而更改,如队列状况可能为(w3, w5, w2, w4, w6, w9, w8).

  C段和D段间的代码将w3, w5, w2, w4整个的batch写入log和memtable. 到E段时,分别对w5, w2, w4进行了一次cond signal.当判断到完w4 == lastwriter时,则退出E段代码。F段则对队首的w6唤醒,从而按上述步骤依次进行下去。

  这样就形成了多个并发write 合并为一个batch写入log和memtable的机制。

原文地址:https://www.cnblogs.com/mh1092/p/9936484.html

时间: 2024-10-04 22:20:50

[LevelDB] 写批处理过程详解的相关文章

使用HeartBeat实现高可用HA的配置过程详解

使用HeartBeat实现高可用HA的配置过程详解 一.写在前面 HA即(high available)高可用,又被叫做双机热备,用于关键性业务.简单理解就是,有2台机器 A 和 B,正常是 A 提供服务,B 待命闲置,当 A 宕机或服务宕掉,会切换至B机器继续提供服务.常见的实现高可用的开源软件有 heartbeat 和 keepalived. 这样,一台 web 服务器一天24小时提供web服务,难免会存在 web 服务挂掉或服务器宕机宕机的情况,那么用户就访问不了服务了,这当然不是我们期望

Nginx实现集群的负载均衡配置过程详解

Nginx实现集群的负载均衡配置过程详解 Nginx 的负载均衡功能,其实实际上和 nginx 的代理是同一个功能,只是把代理一台机器改为多台机器而已. Nginx 的负载均衡和 lvs 相比,nginx属于更高级的应用层,不牵扯到 ip 和内核的修改,它只是单纯地把用户的请求转发到后面的机器上.这就意味着,后端的 RS 不需要配置公网. 一.实验环境 Nginx 调度器 (public 172.16.254.200 privite 192.168.0.48)RS1只有内网IP (192.168

LAMP架构搭建以及基于LAMP架构的主流论坛和博客搭建过程详解

了解网站架构的朋友都知道,现在很多网站的架构都是采用LAMP(Linux+Apache+Mysql/Mariadb+Php)的,至于LAMP架构本身我们就不做过于深入的探讨了,今天我给大家分享的是关于如何搭建LAMP构架,以及如何基于lamp架构去搭建目前国内比较流行的两大开源论坛(phpwind.discuz)一大开源博客(wordpress),通过这个过程也就能让大家明白我们经常上的论坛以及博客,包括包括我们访问的各个网站到底是如何工作起来的. 注意:为了方便给大家展示实验效果,我们就直接关

uboot主Makefile分析(t配置和编译过程详解)

1.编译uboot前需要三次make make distcleanmake x210_sd_configmake -j4 make distclean为清楚dist文件. make x210_sd_config  跳转执行mkconfig用来配置并生成config.mk(board/samsung/x210目录下为指定链接地址的与主uboot目录的config.mk不同) autuconfig.mk 2.框图 3.uboot主Makefile分析 3.1.uboot version确定(Make

Hadoop MapReduce执行过程详解(带hadoop例子)

https://my.oschina.net/itblog/blog/275294 摘要: 本文通过一个例子,详细介绍Hadoop 的 MapReduce过程. 分析MapReduce执行过程 MapReduce运行的时候,会通过Mapper运行的任务读取HDFS中的数据文件,然后调用自己的方法,处理数据,最后输出.Reducer任务会接收Mapper任务输出的数据,作为自己的输入数据,调用自己的方法,最后输出到HDFS的文件中.整个流程如图: Mapper任务的执行过程详解 每个Mapper任

Linux(RHEL6)启动过程详解

Linux(红帽RHEL6)启动过程详解: RHEL的一个重要和强大的方面是它是开源的,并且系统的启动过程是用户可配置的.用户可以自由的配置启动过程的许多方面,包括可以指定启动时运行的程序.同样的,系统关机时所要终止的进程也是可以进行组织和配置的,即使这个过程的自定义很少被需要. 理解系统的启动和关机过程是如何实现的不仅可以允许自定义,而且也可以更容易的处理与系统的启动或者关机相关的故障.  1.启动过程  以下是启动过程的几个基本阶段:   ① 系统加载并允许boot loader.此过程的细

[转载] Android签名机制之—签名过程详解

本文转载自: http://www.wjdiankong.cn/android%E7%AD%BE%E5%90%8D%E6%9C%BA%E5%88%B6%E4%B9%8B-%E7%AD%BE%E5%90%8D%E8%BF%87%E7%A8%8B%E8%AF%A6%E8%A7%A3/ 一.前言 又是过了好长时间,没写文章的双手都有点难受了.今天是圣诞节,还是得上班.因为前几天有一个之前的同事,在申请微信SDK的时候,遇到签名的问题,问了我一下,结果把我难倒了..我说Android中的签名大家都会熟悉

Android签名机制之---签名过程详解

一.前言 又是过了好长时间,没写文章的双手都有点难受了.今天是圣诞节,还是得上班.因为前几天有一个之前的同事,在申请微信SDK的时候,遇到签名的问题,问了我一下,结果把我难倒了..我说Android中的签名大家都会熟悉的,就是为了安全,不让别人修改你的apk,但是我们真正的有了解多少呢?所以准备两篇文章好好介绍一下Android中签名机制. 在说道Android签名之前,我们需要了解的几个知识点 1.数据摘要(数据指纹).签名文件,证书文件 2.jarsign工具签名和signapk工具签名 3

uboot配置和编译过程详解【转】

本文转载自:http://blog.csdn.net/czg13548930186/article/details/53434566 uboot主Makefile分析1 1.uboot version确定(Makefile的24-29行) Makefile代码部分: [plain] view plain copy VERSION = 1 PATCHLEVEL = 30 SUBLEVEL = 4 EXTRAVERSION = U_BOOT_VERSION = $(VERSION).$(PATCHL