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#开源 ...
随机推荐
- maven将jar包添加到本地仓库
mvn install:install-file -Dfile=jar包本地路径 -DgroupId=groupID -DartifactId=ifactId -Dversion=版本 -Dpacka ...
- Hzoi 2018.2.11多边形 区间DP
给定一个由N个顶点构成的多边形,每个顶点被赋予一个整数值,而每条边则被赋予一个符号:+(加法运算)或者*(乘法运算),所有边依次用整数1到N标识. 一个多边形的图形表示 首次移动,允许将某条边删除: ...
- python-windows环境安装
windows下python环境安装 1. 先在官网上下载安装包,官网地址:https://www.python.org/ 点击windows,进入下载选择 选择python3.7.0,点击进入下载, ...
- 第一个go程序和基本语法
目录 第一个go程序和基本语法 一. 第一个go程序 二. 基础语法 1. 命名 2. 变量 3 常量与枚举 4. 数据类型 5. fmt包的使用 6. 类型别名 7. 类型转换 8. 运算符 第一个 ...
- 使用Layer完成图片放大功能
序言:在写这个功能之前也用了zoom.js,zoom.js用起来简单引用js然后设置图片属性就可以放大.但是放大后的图片模糊.没有遮罩.在放大图片时其它图片布局会受到影响,当然如果觉得这些都是小问题的 ...
- 通用 mapper
一.为什么需要通用 mapper 插件 通用 mapper 插件可以自动的生成 sql 语句. 虽然 mybatis 有逆向工程,可以直接生成 XxxMapper.xml 文件,但是这种生成的方式存在 ...
- TCP连接之未连接队列的理解
tcp服务器在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认 ...
- RestTemplate使用详解
1.RestTemplate添加RequestHeader如content-type可通过httpclient设置 List<Header> headers = new ArrayList ...
- POJ 1021 人品题
报告见代码.. #include <iostream> #include <cstdio> #include <cstring> #include <algo ...
- HDU 1599
裸的FLOYD 求最小环. #include <iostream> #include <cstdio> using namespace std; ; ; int n,m,min ...