Home  >  Article  >  Database  >  每天进步一点点leveldb项目实践

每天进步一点点leveldb项目实践

WBOY
WBOYOriginal
2016-06-07 15:28:571213browse

转载请说明出处:http://blog.csdn.net/cywosp/article/details/20746879 leveldb是Google公司开源的高性能Key-Value数据库,在很多开源的项目中就有使用,例如:Chromium,淘宝的Tair,SSDB等。leveldb对于小数据在写入百万个之后读写性能依旧强劲,高效,因

转载请说明出处:http://blog.csdn.net/cywosp/article/details/20746879
leveldb是Google公司开源的高性能Key-Value数据库,在很多开源的项目中就有使用,例如:Chromium,淘宝的Tair,SSDB等。leveldb对于小数据在写入百万个之后读写性能依旧强劲,高效,因此非常适合用于存储小文件,以及一些需要持久化的索引和需要持久化的异步任务。
接触leveldb已经有一段时间了,当初为了找到一款轻量,高性能的数据库用于存储分布式文件系统中的任务,以便于在程序因为未知Bug而崩溃时能保存未完成的异步操作任务,我们网上搜索资料并比较了多个开源数据库,例如:Mysql、Sqlite、leveldb等等,综合各方面,最终选择了leveldb,其key具有排序功能,以及高效的性能非常符合我们的要求(Mysql对于存储本地进程中的任务太过于笨重了,而Sqlite在数据量大情况下性能会有比较明显的变化)。在我们的分布式文件系统中,我们对无需全局共享异步操作任务都使用了leveldb做持久化,在程序重启后重新加载leveldb中的数据,恢复崩溃之前的状态,继续自动执行未完成的任务。我们的大概做法是:为每一个任务分配一个唯一的Key,因为所有的任务都是具有先后执行顺序的,为了能够重启之后,任务的顺序能先前的一样,我们充分利用了leveldb根据字符比较来排序Key的特性,为任务从零开始分配Key的初始值,然后在所分配到的整数前以补0的方式格式化出一个21位的字符串类型的Key值(为什么是21位呢?因为在64位的操作系统中,一个无符号长整数的最大值有20位。任务多了不会出现整型溢出吗?这个不用担心,即使每秒能处理一百万个任务,20位的整型数足矣用N万年)。在系统中不同的模块有不同的任务,甚者同一模块中也有不同的任务类型。那么在从leveldb中读取数据重建任务队列时任务的区分就需要很重要了,因此我们引入了任务类型,将任务类型与任务的其他数据一起序列化保存到leveldb中,在反序列化时先读取任务类型,然后再根据任务类型来调用对应的反序列化函数。根据以上这些需求和做法,我们设计并实现了一套通用的类似于标准库的持久化容器(list、map)来更好的完成所需的功能。
在使用leveldb的过程中我们也遇到了一些问题,同时根据自己的实验以及网上的资料简单总结了一下: 1. 存入leveldb中的每个key所对应的内容不宜太大,超过32KB性能就会下降很快 2. 能够多个key内容合并写入的据尽量使用WriteBatch,这样会使得leveldb顺序写入文件中,性能更快。(前提是内存要做限制) 3. 将db/version_set.cc中的kTargetFileSize值变大,使得在大量数据写入后创建少量的文件 4. 一块磁盘最好只使用一个leveldb实例进行数据的读写,这样可以减少磁道寻址时间 5.由于leveldb是一个先写log,然后再通过后台线程将log文件压缩到对应的文件中,在这个过程中会产生一些临时的文件,从而使得磁盘的使用空间会比真实写入的内容容量要大,只有这些log文件被处理过后,磁盘空间才会与真实写入的内容大小相当。在1.14的版本中,如果磁盘被后续写入的数据写满了,后台压缩log的线程将会导致程序崩溃,大概会出现如类似下堆栈信息:
(_ZN7leveldb3log6Writer18EmitPhysicalRecordENS0_10RecordTypeEPKcm+0x84) [0x7fd34cdb7d94] (_ZN7leveldb3log6Writer9AddRecordERKNS_5SliceE+0x74) [0x7fd34cdb7f44] (_ZN7leveldb6DBImpl5WriteERKNS_12WriteOptionsEPNS_10WriteBatchE+0x1b3) [0x7fd34cdb2273] (_ZN7leveldb2DB3PutERKNS_12WriteOptionsERKNS_5SliceES6_+0x54) [0x7fd34cdacd94] (_ZN7leveldb6DBImpl3PutERKNS_12WriteOptionsERKNS_5SliceES6_+0x9) [0x7fd34cdacdd9]
不过在最新的1.15版本中,这个问题已得到了解决。 6. 如果磁盘被100%写满了,此时最好不要停止leveldb实例,不然下次就无法使用已有的数据了,当磁盘容量到100%时,原有的leveldb就无法打开了。在快速写入数据时,会产生大量的log文件,等这些log文件被后台压缩线程处理后,将会释放出很多被临时暂用的空间。 7. 在1.15版本中,如果使用NewIterator函数创建了leveldb::Iterator对象而没有delete该对象的话,在程序退出时将会报出如下错误: db/version_set.cc:806: leveldb::VersionSet::~VersionSet(): Assertion `dummy_versions_.next_ == &dummy_versions_' failed. 根据代码分析,这里assert失败的原因主要是为了防止内存泄露。
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:啦啦啦redisNext article:巧用函数索引性能调优