我是一个 op 之 supervisor
1. 简介
平时在开发机上,有一些进程比较稳定,基本不用考虑退出的场景,例如python -m SimpleHTTPServer,我习惯直接使用 nohup 放到后台执行。nohup 使用简单,但是如果进程需要从 stdin 读取输入,例如agedu -w(显示磁盘使用容量),无法使用 nohup 放到后台执行。
后台服务进程需要保证 7*24,因此对稳定性要求较高。进程无论是主动还是意外退出时,都需要一个自动拉起的机制,保证服务可用。supervisor 就是这样一个工具。
更进一步,supervisor 提供了非常方便的管理进程的能力,使得我们可以专注于实现后台服务功能本身,而不用关心如何管理服务进程。
2. 安装及配置
supervisor 是用 python 写的,可...
leveldb笔记之24:leveldb笔记总结
从去年突然想写几篇关于 leveldb 的笔记开始,不知不觉已经过去五个月了,原定的几篇也不小心写成了二十多篇。
这篇笔记作为一个总结篇,整体介绍下。
1. 整体架构
我尝试把所有笔记画成了一张图,包含了重要的结构及流程。从最开始的 leveldb 读写流程逐渐放大,包含 memtable sstable 等的结构,以及更细节的部分。
其实可以画的更细,不过因为太复杂放弃了,感兴趣的话可以直接看下笔记.
简单来讲,文件结构上跟BigTable里 Tablet Representation 部分很像:
2. LSM
提起 leveldb,就不得不说 LSM. leveldb 是 LSM 的一个典型实现。
The Log-Structured Merge-Tree ...
leveldb笔记之23:谈谈leveldb的一些魔数
代码里经常会有一些奇怪的数字设置,我们称之为魔数(magic number)。这些数字有的没有意义,有的则包含了线上实际经验。很多时候想清楚这些数字设置的原则,对于充分理解一个模块,以及线上优化,都有重要的作用。
这篇笔记,聊聊 leveldb 里的那些数字。
经验不足,有些只是纯粹靠着代码臆测,不对之处还请指正。
1. memtable 的最大大小
option.write_buffer_size表示一个 memtable 的最大大小,默认为 4M.
2. level 0 的文件大小及个数
level 0 的文件是无序的,每次查找 level 0都需要查找所有文件,因此需要严格控制个数。
当文件数目 >= config::kL0_SlowdownWritesTri...
扯扯 cpu idle 与 load average
最近值周遇到一个系统负载的问题,回溯了下问题大概已经持续了两个多月,这篇笔记记录下对于系统 CPU 负载的理解,不定期根据线上经历更新。
1. idle 与 load
idle 表示 cpu 的闲置程度,数值越大表示 cpu 负载越低。对应的,load average 表示 CPU 的利用率,系统记录了过去 1min/5min/15min 的记录。
通过 top 可以看到这两个数值:
更细的,可以看到每个 cpu 的 idle:
严格来讲,load average 是指运行队列的平均长度,也就是等待 cpu 的进程数。这些进程是系统中的活动进程,也就是处于 TASK_RUNNING or TASK_UNINTERRUPTIBLE 的进程数。
系统的进程状态有这么几种...
神奇的锟斤拷
上个周末接到电话,说是线上有“锟斤拷”的结果出现,不再做电子书解析这些事情后,对这类问题逐渐淡忘了,这篇笔记就介绍下神奇的“锟斤拷”是如何产生的。
很多系统间的转换,例如编码的转换、时间的转换等,在我看来也是程序员的基本技能之一,比如当你开发一个评论系统时,不同时区的人回复后,应当怎么样记录这些时间,并且提供给别人用的时候最大可能的保证不会出错?
关于编码,有一次让我印象很深刻的 code review,之前在多看的时候,大概写了这么一段代码和注释:
//最多允许填充63个字符或者31个汉字
char buffer[64];
...
场景模糊记得是直接设置返回到前端一些提示语,用于在线更新时的提示,所以额外注释了下能写多少个汉字。code review 给的意见是希望限制的字...
leveldb笔记之22:Better Code
本文总结下 leveldb 一些好的代码习惯。
1. 返回信息丰富
leveldb::Status除了返回码,可以提供更丰富的返回信息。
例如,leveldb::DB::Put接口的返回值:
virtual Status Put(const WriteOptions& options,
const Slice& key,
const Slice& value) = 0;
内部使用const char*来记录全部内容:
// OK status has a null state_. Otherwise, state_ is a new[] array
/...
leveldb笔记之21:二分法
二分法是个很有意思的话题,思路很简单,也能极大的提高程序的性能,之前我们介绍过如何写出正确的二分法以及分析
。因为二分法在 leveldb 使用非常常见,这篇笔记炒个冷饭,再结合着 leveldb 源码讲下二分法最重要的一个概念:循环不变式。
1. Block 内的 Seek
当我们查找 target 是否存在于某个 data block,用的就是二分法,二分的对象是 restart points.
restart points 将 entry 分为多组:
每组包含多个key:value数据,每一个 restart point 指向一组里最小的那个 entry,我们称为 min-key entry.
Seek的目标是找到第一个 >= target 的 entry.
...
leveldb笔记之20:写入与读取流程
这篇笔记介绍下写入和读取过程,前面已经铺垫了很多基础组件,写入介绍起来相对简单一些了。
1. Put
先用一张图片介绍下:
写入的key value首先被封装到WriteBatch
// Default implementations of convenience methods that subclasses of DB
// can call if they wish
Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {
WriteBatch batch;
//key,value数据更新到batch里
batch.Put(key,...
leveldb笔记之19:major compaction
minor compaction主要解决内存数据持久化磁盘。major compaction 则负责将磁盘上的数据合并,每合并一次,数据就落到更底一层。
1. 作用
随着数据合并到更大的 level,一个明显的好处就是清理冗余数据。
如果不同 level 的 sst 文件里,存在相同的 key,那么更底层的数据就可以删除不再保留(不考虑 snapshot的情况下)。为了充分利用磁盘高性能的顺序写,删除数据也是顺序写入删除标记,而真正删除数据,是在 major compact 的过程中。
所以,一个作用是能够节省磁盘空间。
level 0 的数据文件之间是无序的,每次查找都需要遍历所有可能重叠的文件,而归并到 level 1 之后,数据变得有序,待查找的文件变少。
所以,另外...
leveldb笔记之18:Iterator
Iterator 的思想在 stl 很常见,通过迭代器,可以实现算法和容器的解耦。当我们实现某个算法时,只需要关注迭代器的常见操作,例如++ --等,而不用关心作用的具体容器是 map 还是 vector.
在 leveldb 的代码里,迭代器的应用也非常普遍,接口上变成了next prev等,在我看来主要有两个好处:
通用的设计:我们已经都习惯了迭代器的使用,通过Iterator来提供遍历数据的接口,学习成本低,protobuf里也在大量使用。
2.易于实现:在不同的数据上,实现相同的Iterator接口,使得在操作这些不同数据时,可以统一使用Iterator.这个跟 stl 里的思想是一致的。
正是由于大量的使用,理解leveldb 的Iterator,对通顺的阅读源...
leveldb笔记之17:major compaction之筛选文件
继续接着上一篇笔记的结尾讲,这篇笔记介绍下筛选参与 major compaction 文件的过程,算是介绍 major compaction 的一个开端。
1. 当我们谈论筛选时,在谈论什么
leveldb 最为复杂的在 compaction,compaction 最为复杂的在 major compaction.面对磁盘上的众多 sstable 文件,应该怎么开始?
千里之行始于足下,首先需要找到最应该 compact 的一个文件。
“最应该”的判断条件,前面笔记已有介绍,有seek_compaction && size_compaction,分别从读取和文件大小两个维度来判断。
筛选出这个文件后,还需要考虑一系列问题:
如果在 level 0,由于该...
260 post articles, 22 pages.