leveldb在.Net中的使用
前言
Leveldb是一个google实现的非常高效的kv数据库,目前的版本1.2能够支持billion级别的数据量了。 在这个数量级别下还有着非常高的性能,主要归功于它的良好的设计。特别是LSM算法。[节选自百度百科]
leveldb的功能十分强大,并且我司的项目上使用到了leveldb,但是无奈有一个bug。借本篇简书,对bug的解决以及leveldb的使用一同进行编写。
使用方法
本示例在Visual Studio 2015下进行编译通过。请笔者在阅读之前先准备好环境。
安装leveldb.net库
在Visual Studio中有一个非常好用的工具nuget(Tools -> NuGet Package Manager),可以在菜单里面对nuget安装的库进行安装和卸载,也可以通过命令行进行安装库。
使用方法
根据面向对象的三大准则:封装、继承和多态,对于该库的使用,必须要写一个类来对其进行二次封装,即编写一个工具类,这样在上层使用会更方便一些。
- 建立LeveldbUtil类,并且引入leveldb库
using LevelDB;
- 单例的实现
private static LeveldbUtil _instance = null;
private static readonly object synObject = new object();
private LevelDB.DB levelDB;
private string dbName = "data";
private LeveldbUtil()
{
Options options = new Options()
{
CreateIfMissing = true,
};
this.levelDB = LevelDB.DB.Open(this.dbName, options);
}
public static LeveldbUtil Instance
{
get
{
if (null == _instance)
{
lock (synObject)
{
if (null == _instance)
_instance = new LeveldbUtil();
}
}
return _instance;
}
}
这里单例的实现使用到static变量来实现,在实现的过程中需要进行加锁,保证该代码被执行一次。
- 实现数据库的增、删和查操作
public void writeData(string key, byte[] value)
{
this.levelDB.Put(new WriteOptions(), key, value);
}
public void delDataWithKey(string key)
{
this.levelDB.Delete(new WriteOptions() { Sync = true }, key);
}
public byte[] getFirstValue(ref string key)
{
Iterator iterator = this.levelDB.NewIterator(new ReadOptions());
iterator.SeekToFirst();
if (!iterator.Valid())
return null;
key = iterator.Key().ToString();
byte[] res = iterator.Value().ToArray();
iterator.Dispose();
return res;
}
public byte[] getValueWithKey(string key)
{
byte[] val = this.levelDB.Get(new ReadOptions(), key).ToArray();
return val == null ? null : val;
}
在getFirstValue中使用到了iterator,一定要注意的是使用完之后要进行Dispose,否则程序执行完成之后会报“System.AccessViolationException”错误,即访问了非法的内存空间。
另外一个操作是对数据库进行统计数目和大小,代码如下:
public void getDataSize(ref long dataCount, ref long dataSize)
{
dataCount = 0;
dataSize = 0;
Iterator iterator = this.levelDB.NewIterator(new ReadOptions());
iterator.SeekToFirst();
while (iterator.Valid())
{
++dataCount;
byte[] valBytes = Convert.FromBase64String(iterator.Value().ToString());
dataSize += valBytes.LongLength;
iterator.Next();
}
iterator.Dispose();
}
同样的iterator也需要进行Dispose。在使用结束数据库后,将数据库关闭,
public void closeDB()
{
this.levelDB.Dispose();
}
这样,leveldb的工具类便完成,使用就比较简单,唯一再次说明的就是,在使用完成之后一定要调用closeDB,否则数据有可能留在内存中,造成索引和数据的丢失。
结束语
本文对leveldb在.net进行了工具类的实现。笔者在实现的过程中遇到了许多问题,例如单例忘了加lock,在使用iterator过程中未将iterator进行释放,还有就是忘记关闭数据库。其中iterator未释放这个问题解决了好久,因为程序在运行结束后报内存访问错误,因此没有意识到这个问题。毕竟leveldb是由C++编写,需要严格的按照“谁分配谁回收”的原则使用内存。在这里也算是做一个总结,希望读者在使用的过程中可以少走些弯路,更快速的完成需求,完成项目。
最后,demo已经托管至github。
https://github.com/njim3/LevelDBDemo.git