LevelDB剖析(7):并发模型 和 MVCC

LevelDB是线程安全的,即在同一进程内允许用户起多个线程并发调用同一DB实例上的任何接口,但这并不意味着相应吞吐和性能的提升. 我们最后来看下LevelDB支持的并发模型以及其性能瓶颈所在.

下图再次展示了LevelDB上的几个主要操作以及涉及到的结构:
leveldb_concurrecy

1) Compaction是内部线程,恒为1个;Read/Write以及Compaction自身触发产生的Compaction请求在线程内排队处理.
2) Compaction与Read/Write等操作不存在互斥问题,因此其主要瓶颈在于文件系统IO
3) Write操作既要写log又要写mem,存在一定的写放大;Write事务必须串行执行;Write可能会因等待某些事件而阻塞.
4) Read操作需与Write在memtable上互斥访问,引入了原子操作使得互斥区间极小;Read主要开销在多个sstable上的IO.

由此可见LevelDB采用的是单个Writer,多个Reader,以及内部一个用于Compact的Background线程的并发模型. 因此用户开多个线程进行Write意义不大,因为根据3)内部会做串行化处理. 而如果能合理用上多个磁盘的话,多线程Read可以提高吞吐.

作为一个持久化的KV存储系统,其性能瓶颈必然在IO上无疑,但具体怎么产生的呢. 对Write来说最多只需要写一个log文件,性能和吞吐一般会高于Read;对Read来说需要在多个sstable文件上做合并读取,并且文件越多性能越低,因此需要限制文件数量(主要是Level_0). 而Level_0文件来自imm的dump,而后者又由Write产生,因而需要限制Write的速度:当level_0文件数量较多时,放慢或阻塞Write,直到等Compaction将其数量减少. 由此平衡了Read和Write的IO开销.

可见LevelDB的实际性能瓶颈在最重的Compaction操作, 但对用户来说会首先反应在Write操作上:如果Write过快会引起卡顿、阻塞等问题,需要使用者用其他方式解决,如设法放慢写速度,分布式化等.

最后再来回顾下LevelDB中多版本策略的应用,即MVCC(Multi Version Concurrency Control). 其中一个体现便是在Compaction: 1) table是immutable的,Compaction的同时允许Read等其他操作并发进行;2) 新table在完成之后才会加入到元数据,因而正在创建的table对Read等过程不可见,不会污染正式数据. 由此可见多版本策略提高了系统的整体并发能力. 此外对单个key的多版本支持自然地导出了Snapshot功能.

LevelDB总体设计体现了KISS原则,并且细节处理得很到位. 除本系列重点提级的业务层逻辑外还有跨平台处理、c语言接口、测试框架等内置支持模块,实现也比较精巧,这里不再展开,推荐阅读代码. Facebook的RocksDB基于LevelDB进一步做了很多优化,值得参考. SSDB是采用leveldb引擎的一个服务封装,其协议和接口与redis兼容,也支持分布式.

=== 2017-02-15 Update: 那岩的《LevelDB实现解析》 总结部分写得很好:http://www.docin.com/p-429543446.html

发表评论

电子邮件地址不会被公开。