2013-05-06

LevelDB 写操作出现停顿的问题分析

Views: 31127 | 4 Comments

我写了一个简单的循环程序往 LevelDB 写数据进行测试, 发现偶尔会出现停顿的情况, 单个写操作可能会耗时超过1秒. 这种慢请求是非常致命的, 因为在高并发的 Web 应用中, 1秒钟影响到的用户是成百上千个.

经过分析, 发现 LevelDB 实现了写限速机制, 当 Level-0 的 sst 文件数超过一定数量时开始限速, 再超过一定数量时, 直接主动的阻塞写操作, 直到 Compaction 线程减少了 Level-0 的 sst 文件数量减少到阀值以下.

这几个阀值由 dbformat.h 文件中的几个参数控制:

// Grouping of constants.  We may want to make some of these
// parameters set via options.
namespace config {
// Level-0 compaction is started when we hit this many files.
static const int kL0_CompactionTrigger = 4;

// Soft limit on number of level-0 files.  We slow down writes at this point.
static const int kL0_SlowdownWritesTrigger = 8;

// Maximum number of level-0 files.  We stop writes at this point.
static const int kL0_StopWritesTrigger = 12;

这些参数是编译时控制, 不能动态更改, 虽然 Google 计划后续实现可动态更改. 默认当 Level-0 的 sst 文件数量达到4个(kL0_CompactionTrigger)时, 启动 Compaction 线程, 这时会边写边合并, 如果写的速度超过了合并的速度, 那么 sst 文件的数量会持续增加, 当达到8个(kL0_SlowdownWritesTrigger)时, LevelDB 主动 sleep 1 毫秒, 以降低写的速度. 如果 sst 还是继续增加, 达到 12 个(kL0_StopWritesTrigger)时, 写就完全停止了, 也就是不能再写了.

我已经将这个问题向 LevelDB 的官方反馈, 目前还没有得到回复: https://code.google.com/p/leveldb/issues/detail?id=164&sort=-id

继续问题已经查明, 那么就可以通过下面的方法避免这个问题:

  1. 加大这几个参数, 应对突发的写请求. 最新版本的 SSDB 暂时采用了这种方案, 将后两个参数改为 16 和 64, 再加上 SSDB 的单个 sst 文件大小是 32MB, 只有在极端写的条件下才会触发(Trigger)这些限速条件.
  2. 修改 db_imple.cc 文件, 将限速机制禁用.
       } 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.
          env_->SleepForMicroseconds(1000);
    ...
        } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {
          // There are too many level-0 files.
          Log(options_.info_log, "waiting...\n");
          bg_cv_.Wait();
    

    目前, 禁用限速机制会导致什么样的不良影响不不清楚, 我在等待 LevelDB 官方的回复. 补充: 如果 Level-0 的 sst 文件数量过多, 会导致 Compaction 时占用(临时)过多内存.

LevelDB 是一个性能非常优秀的 KV 存储引擎, 不过, 跟所有的解决方案一样, 它也有一些缺陷, 只能在使用过程中发现和改进.

Related posts:

  1. LevelDB 服务器 SSDB 支持主从(master-slave)同步了!
  2. SSDB 解决了 Snappy 导致 LevelDB 编译失败的问题
  3. SSDB – 支持 zset 的 LevelDB 服务器
  4. LevelDB 会丢数据吗?
  5. SSDB 增加了 Compaction 限速功能
Posted by ideawu at 2013-05-06 13:51:55 Tags: ,

4 Responses to "LevelDB 写操作出现停顿的问题分析"

  • 您好,我这里有几个问题请教一下。
    1. 除了修改kL0_CompactionTrigger和kL0_StopWritesTrigger,还有对levelDB做其他的修改么?
    2. compaction的限速是在leveldb中修改还是在ssdb服务器中进行的呢?
    3. 对于levelDB的level层数有什么修改或者看法的么?原levelDB是level-0 ~ level6,共7层,减少一些层数是否对于一些热数据比较多的场景读取有好处呢? Reply
  • 我们开始测试LevelDB的时候,只调用LevelDB的put/get/del接口,sst都是4MB的,只是临时修改了log的大小到1G,发现sst也是1G Reply
    @ruochen: 修改log的大小, 只能顺带改变level-0的sst的大小, 一旦被合并到level-1及以上时, 大小又会恢复. Reply
  • 长时间连续写居然会遇到block,看来也得好好看下源码 Reply

Leave a Comment