levelDB数据库使用及实例 - 高性能nosql存储数据库
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存储数据库的更多相关文章
- 什么时候该使用NoSQL存储数据库?
原文地址:http://www.jdon.com/39240 文章总结以下几点:1.频繁写,很少读统计数据,比如点击率,应该使用基于内存的in-memory的key/value存储数据库如Redis, ...
- 发布一个参考ssdb,使用go类似的实现redis高性能nosql:ledisdb
起因 ledisdb是一个參考ssdb.採用go实现,底层基于leveldb,相似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们如今的应用极大的依 ...
- 发布一个参考ssdb,用go实现的类似redis的高性能nosql:ledisdb
起因 ledisdb是一个参考ssdb,采用go实现,底层基于leveldb,类似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们现在的应用极大的依 ...
- 一、初识MySQL数据库 二、搭建MySQL数据库(重点) 三、使用MySQL数据库 四、认识MySQL数据库的数据类型 五、操作MySQL数据库的数据(重点)
一.初识MySQL数据库 ###<1>数据库概述 1. 数据库 长期存储在计算机内的,由组织的可共享的数据集合 存储数据的仓库 文件 ...
- 选择高性能NoSQL数据库的5个步骤
来源:Redislabs作者:Shabih Syed 翻译:Kevin (公众号:中间件小哥) 构建在线和运营应用程序的开发团队越来越多地选择一类新的数据库来支持它们.它被称为“NoSQL”或“Not ...
- 高性能nosql ledisdb设计与实现(1)
ledisdb是一个用go实现的基于leveldb的高性能nosql数据库,它提供多种数据结构的支持,网络交互协议参考redis,你可以很方便的将其作为redis的替代品,用来存储大于内存容量的数据( ...
- (转)[转]大数据时代的 9 大Key-Value存储数据库
在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...
- 大数据时代的 9 大Key-Value存储数据库
在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...
- .NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB
今天给大家介绍一个不错的小巧轻量级的NoSQL文件数据库LiteDB.本博客在2013年也介绍过2款.NET平台的开源数据库: 1.[原创]开源.NET下的XML数据库介绍及入门 2.[原创]C#开源 ...
随机推荐
- Hadoop 使用小命令(2)
一.查看一堆文件共有多少行 查看file1/file2目录下所有文件总共多少行 hadoop fs -text file1/file2/* | wc -l 二.正则表达式 hadoop fs -tex ...
- 【剑指Offer】34、第一个只出现一次的字符
题目描述: 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写). 解题思路: ...
- 【剑指Offer】12、数值的整数次方
题目描述: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 解题思路: 本题看似比较简单,是一个简单的指数运算,但需要完 ...
- 15.3 Task Task.Yield和Task.Delay说明
https://blog.csdn.net/hurrycxd/article/details/79827958 书上看到一个Task.Yield例子,Task.Yield方法创建一个立即返回的awai ...
- layer 使用教程
http://layer.layui.com/ <!DOCTYPE html><html lang="en"><head> <meta c ...
- opencv学习HighGUI图形用户界面初步【1】
HighGUI是图形用户界面模块.包括:1.输入与输出:2.视频捕捉:3.图形和视频的解码编码:4.图形交界面与接口. 由于opencv.hpp包含了core.objdetect.ingproc.ph ...
- 【Zoj 4061】Magic Multiplication
[链接] 我是链接,点我呀:) [题意] [题解] /* for a[1] from 1~9 1*1=1 2*1=2 3*1=3 1*2=2 2*2=4 3*2=6 1*3=3 2*3=6 3*3=9 ...
- LID&LDS 的另外一种算法
参见:LIS,LDS的另类算法(原) 然后讲讲我的想法: 有结论不上升子序列的个数=最长上升子序列的长度.....至于为什么,在下面讲 上代码: #include <iostream> ...
- ASP的Global.asa使用说明
/*-------------------ASP文档参考集-----------------------*/ *-->作者:草履虫 *-->时间:2007-4.28---2007-4.30 ...
- MySQL架构优化实战系列3:定时计划任务与表分区
原创 2016-07-08 汤抗 DBAplus社群 一 定时计划任务 1.概论 mysql计划任务可以定时更新数据库表或者做大文件的汇总表. 2.配置 开启计划任务 SHOW VARIABL ...