我写了一个简单的循环程序往 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
继续问题已经查明, 那么就可以通过下面的方法避免这个问题:
- 加大这几个参数, 应对突发的写请求. 最新版本的 SSDB 暂时采用了这种方案, 将后两个参数改为 16 和 64, 再加上 SSDB 的单个 sst 文件大小是 32MB, 只有在极端写的条件下才会触发(Trigger)这些限速条件.
- 修改 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 存储引擎, 不过, 跟所有的解决方案一样, 它也有一些缺陷, 只能在使用过程中发现和改进.
1. 除了修改kL0_CompactionTrigger和kL0_StopWritesTrigger,还有对levelDB做其他的修改么?
2. compaction的限速是在leveldb中修改还是在ssdb服务器中进行的呢?
3. 对于levelDB的level层数有什么修改或者看法的么?原levelDB是level-0 ~ level6,共7层,减少一些层数是否对于一些热数据比较多的场景读取有好处呢? Reply