LevelDB是google公司开发出来的一款 超高性能kv存储引擎,以其惊人的读性能和更加惊人的写性能在轻量级nosql数据库中鹤立鸡群. 此开源项目目前是支持处理十亿级别规模Key-Value型数据持久性存储的C++ 程序库。在优秀的表现下对于内存的占用也非常小,他的大量数据都直接存储在磁盘上.可以理解为以空间换取时间.

任何东西都不是十全十美的,LevelDB也有它的局限性:

LevelDB 只是一个 C/C++ 编程语言的库, 不包含网络服务封装, 所以无法像一般意义的存储服务器(如 MySQL)那样, 用客户端来连接它, 使用者应该封装自己的网络服务器.

node.js下如何使用LevelDB ?

node.js环境下使用需要npm 包 levelUP,levelDown 来支持. npm install levelUP levelDown 或者你也可以这样 npm install level 提醒:levelup 版本最好用0.10.0或者更低版本,最新版本和leveldown编译时出问题. npm 安装指定版本依赖包 所以这样比较安全 npm install levelup@0.10.0 leveldown

如何使用?

api定义非常简单. var levelup = require('levelup'); var db = levelup('./yijiebuyi'); //这里的路径就是物理存储数据的文件路径,建议不要放到项目中. 下面是常用的获取,设置api (引用自 github node-levelup https://github.com/rvagg/node-levelup)

    // put a key & value
db.put('name', 'LevelUP', function (err) {
if (err) return console.log('Ooops!', err) // some kind of I/O error // fetch by key
db.get('name', function (err, value) {
if (err) return console.log('Ooops!', err) // likely the key was not found // ta da!
console.log('name=' + value)
})
})

一介布衣博客 采用了node.js + leveldb 方式,上面的这个数据库封装类也是一介布衣博客使用的一个通用帮助文件.

关于levelDB的api我简单做了一个封装,代码如下:

//设置
function put(key, value, callback) {
if (key && value) {
db.put(key, value, function (error) {
callback(error);
})
} else {
callback('no key or value');
}
} //获取
function get(key, callback) {
if (key) {
db.get(key, function (error, value) {
callback(error, value);
})
} else {
callback('no key', key);
}
}
//删除
function del(key, callback) {
if (key) {
db.del(key, function (error) {
callback(error);
})
} else {
callback('no key');
}
}
//批量操作
function batch(arr, callback) {
if (Array.isArray(arr)) {
var batchList = [];
arr.forEach(item)
{
var listMember = {};
if (item.hasOwnProperty('type')) {
listMember.type = item.type;
}
if (item.hasOwnProperty('key')) {
listMember.key = item.key;
}
if (item.hasOwnProperty('value')) {
listMember.value = item.value;
}
if (listMember.hasOwnProperty('type') && listMember.hasOwnProperty('key') && listMember.hasOwnProperty('value')) {
batchList.push(listMember);
}
}
if (batchList && batchList.length > 0) {
db.batch(batchList, function (error) {
callback(error, batchList);
})
} else {
callback('array Membre format error');
}
} else {
callback('not array');
}
}
//查找 (支持前置匹配)
function find(find, callback) {
var option = {keys: true, values: true, revers: false, limit: 20, fillCache: true};
if (!find)
return callback('nothing', null);
else {
if (find.prefix) {
option.start = find.prefix;
option.end = find.prefix.substring(0, find.prefix.length - 1)
+ String.fromCharCode(find.prefix[find.prefix.length - 1].charCodeAt()+1);}if(find.limit)
option.limit = find.limit; db.createReadStream(option).on('data',function(data){
data&&callback(data.key, data.value);}).on('error',function(err){}).on('close',function(){}).on('end',function(){return callback(null,Date.now());});}} exports.put = put;
exports.get=get;
exports.del=del;
exports.find = find;
exports.batch = batch;

代码注释的不详细,我直接拷贝过来的,方法名基本保持和api一致,大家应该能看明白 里面的 get ,put ,delete 都非常好理解,就是根据key去查询value ,插入一对key和value ,根据key删除value. find 方法可能不是特别好理解,在levelDB存储复杂数据结构中讲到用前置匹配的方法来查询索引正是用到了createReadStream 方法,这是原生api里的方法,我利用这个api封装成了 find() 方法.下面主要说一下前置匹配.

createReadStream 方法的前置匹配

回顾一下,我库里有如下key-value 键值对 abc --> 111 abd --> 333 abm --> 777 abw --> 999 那么我可以用createReadStream方法查询出以 ab开头的key 对应的所有value,安装key的读取顺序把value一个一个返回.我们贴出createReadStream调用代码:

db.createReadStream()
.on('data', function (data) { console.log(data.key, '=', data.value)
})
.on('error', function (err) { console.log('Oh my!', err)
})
.on('close', function () { console.log('Stream closed')
})
.on('end', function () { console.log('Stream closed')
})

它监听了一个读取流,要触发如下事件 on(‘data’ 将读取到的value按照读取顺序返回. on(‘error’ 发生错误时要做什么 on(‘close’ 关闭流时要做什么 on(‘end’ 流结束时做什么 其实这个api可以接受一个参数,参数可以指定key的开始位置 ,结束位置,然后就可以做到前置匹配,我把此方法封装了一下,在levelup使用方法一文中已经贴出了代码,下面再单独把此方法的封装贴一下

function find(putoption, callback) {
var option = {keys: true, values: true, revers: false, limit: 20, fillCache: true};
if (!putoption)
return callback('nothing', null);
else {
if (putoption.prefix) {
option.start = find.prefix;
option.end = find.prefix.substring(0, find.prefix.length - 1)
+ String.fromCharCode(find.prefix[find.prefix.length - 1].charCodeAt() + 1);
} if (putoption.limit)
option.limit = find.limit; db.createReadStream(option).on('data',function (data) {
data&&callback(data.key, data.value);
}).on('error',function (err) {
}).on('close',function () {
}).on('end', function () {
return callback(null, Date.now());
});
}
}

putoption 是外部出入的参数对象, option 是内部组合的参数对象, createReadStream 可以指定读取key的开始位置(如 option.start ),结束位置(option.end ),以及读取多少条记录 (option.limit)

使用场景,就拿一介布衣博客博文存储的数据结构来说起:

一介布衣 博客中有nosql分类,点击nosql应该加载此分类下的所有博文并分页. 首先,博文的存储类型如下 key: blog.fse89fw8fwe89fwe98fweiwe9f value: {_id:’fse89fw8fwe89fwe98fweiwe9f’,title:’levelDB存储复杂数据结构’,content:’省略500字’,category:’nosql’,tag:’nosql,levelDB,levelDB存储结构’,create_time:13987546090,click:10}

如上,key为了全部全局唯一 所以用常量 blog.+博文唯一ID value 是一个字符串,我们读出来后需要反序列化成json来使用,包括唯一ID,标题,内容,类别,标签,创建时间和点击数

到这里并没有结束,你还需要做很多工作,比如我根据类别查询博客,根据标签查询博客,根据时间统计,排序等… 所有的kv数据库貌似都是根据key查询value ,而不能逆向查询 (如果有,请告诉我) levelDB也是,所以我们下面要创建一系列的索引来支撑上面的查询工作.

索引的key本人规定 .开头,只是为了维护,你可以命名自己的索引是 芝麻开门 开头或者更奇葩的都行. 类别索引 key : .category.nosql:fse89fw8fwe89fwe98fweiwe9f value : fse89fw8fwe89fwe98fweiwe9f

这个索引的作用就是,通过 nosql 我要找到属于这个类别的博文ID,然后根据ID找到博文 说明一下,key组成结构 常量 .category + 类别名 .nosql + 博文ID fse89fw8fwe89fwe98fweiwe9f value 组成结构 很简单,就是一个博文ID

你也许有疑问:

1.key里面为啥要有 前置常量 .category ,因为我还有标签的索引 .tag 开头的,只是为了区分 2.既然 category 后面已经加上 nosql 类别,为啥还要加一个 博文ID,因为保证此索引是唯一的,我明天继续写一篇关于 levelDB 的文章,他的类别还是 nosql,如果没有这个博文ID,那么就把今天的key完全覆盖了.我的索引是没有意义的. 3.你的索引就是为了查找 博文ID,我需要通过 key 来查找 value,这不是骑着毛驴找毛驴吗? 开始这个问题确实让人迷糊,首先你要同意上面第二点的说明,接着我们来说明如何在没有博文ID的情况下使用此索引找到博文ID 因为levelDB有一项强大的功能,前置匹配,他可以用key的子集去匹配类似的key,注意这里有个前提,必须的从key的0位置开始,就是从头匹配 比如 key是 abcdef 你可以用 a 匹配到这个key,用ad匹配到,用 abc,abcd,abcde 都可以.但是不能用 bcdef来匹配,这样是不行的.

再回头看第三个问题,我根据类别找博文的时候是用下面的key .category.nosql: 用这个key 我可以匹配到 N个key ,这些key对应的博文ID 都是nosql相关的,然后我再遍历博文ID获取博文

levelDB数据库使用及实例 - 高性能nosql存储数据库的更多相关文章

  1. 什么时候该使用NoSQL存储数据库?

    原文地址:http://www.jdon.com/39240 文章总结以下几点:1.频繁写,很少读统计数据,比如点击率,应该使用基于内存的in-memory的key/value存储数据库如Redis, ...

  2. 发布一个参考ssdb,使用go类似的实现redis高性能nosql:ledisdb

    起因 ledisdb是一个參考ssdb.採用go实现,底层基于leveldb,相似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们如今的应用极大的依 ...

  3. 发布一个参考ssdb,用go实现的类似redis的高性能nosql:ledisdb

    起因 ledisdb是一个参考ssdb,采用go实现,底层基于leveldb,类似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们现在的应用极大的依 ...

  4. 一、初识MySQL数据库 二、搭建MySQL数据库(重点) 三、使用MySQL数据库 四、认识MySQL数据库的数据类型 五、操作MySQL数据库的数据(重点)

    一.初识MySQL数据库 ###<1>数据库概述     1. 数据库         长期存储在计算机内的,由组织的可共享的数据集合         存储数据的仓库         文件 ...

  5. 选择高性能NoSQL数据库的5个步骤

    来源:Redislabs作者:Shabih Syed 翻译:Kevin (公众号:中间件小哥) 构建在线和运营应用程序的开发团队越来越多地选择一类新的数据库来支持它们.它被称为“NoSQL”或“Not ...

  6. 高性能nosql ledisdb设计与实现(1)

    ledisdb是一个用go实现的基于leveldb的高性能nosql数据库,它提供多种数据结构的支持,网络交互协议参考redis,你可以很方便的将其作为redis的替代品,用来存储大于内存容量的数据( ...

  7. (转)[转]大数据时代的 9 大Key-Value存储数据库

    在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...

  8. 大数据时代的 9 大Key-Value存储数据库

    在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...

  9. .NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB

    今天给大家介绍一个不错的小巧轻量级的NoSQL文件数据库LiteDB.本博客在2013年也介绍过2款.NET平台的开源数据库: 1.[原创]开源.NET下的XML数据库介绍及入门 2.[原创]C#开源 ...

随机推荐

  1. BZOJ 1738: [Usaco2005 mar]Ombrophobic Bovines 发抖的牛 网络流 + 二分 + Floyd

    Description FJ's cows really hate getting wet so much that the mere thought of getting caught in the ...

  2. 11.5 【Linq 】连接

    11.5.1 使用 join 子句的内连接 如果你打算把一个巨大的序列连接到一个极小的序列上,应尽可能把小序列作为右边序列 class Program { static void Main(strin ...

  3. The story of one latency spike

    转自:https://blog.cloudflare.com/the-story-of-one-latency-spike/ A customer reported an unusual proble ...

  4. CodeForces 245C-Game with Coins

    题意:给你一个n,紧接着n个正数,然后有一种操作:选择一个x满足(x*2+1<=n)一次可以把下标为  x,2*x,2*x+1的三个数同时减一: 问,最少几次操作可以使n个数字变为零(已经是0的 ...

  5. Java 学习(8):java 方法

    Java方法是语句的集合,它们在一起执行一个功能. 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 命名规则: 1. 必须以 字母.'_'或'$'开头. ...

  6. Cloudera 5.8.3 SolrCloud+HDFS的索引数据备份和恢复。(需重启solr进程。)

    一.备份基于HDFS的solrCloud集合数据 1.确认要备份的solr文件夹. /solr/history_customer_collection_test 2.开启HDFS快照功能. hdfs ...

  7. Spring Boot项目@RestController使用重定向redirect

    Spring MVC项目中页面重定向一般使用return "redirect:/other/controller/";即可.而Spring Boot使用了@RestControll ...

  8. PCA降维技术

    PCA降维技术 PCA 降维 Fly Time: 2017-2-28 主成分分析(PCA) PCA Algorithm 实例 主成分分析(PCA) 主成分分析(Principal Component ...

  9. Android解决使用findViewById时须要对返回值进行类型转换问题的辅助类

    在我们的开发工作时,findViewById可能是用得最多的函数之中的一个.但它特别讨厌的地方就是我们常常须要对返回的view进行类型转换,输入麻烦.代码丑陋,比如曾经我们在Activity中找一些子 ...

  10. PbootCMS V1.1.4 正式发布

    PbootCMS V1.1.4 正式发布 PbootCMS V1.1.4 build 2018-06-251.修复自定义表单表名重复仍然添加成功问题:2.修复分享到微信导致页面错误的问题:3.修复静态 ...