文档章节

leveldb.net对象读写封装

泥水佬
 泥水佬
发布于 2014/08/22 10:18
字数 1173
阅读 137
收藏 2
leveldb是一个非常高效的可嵌入式K-V数据库,在.NET下有着基于win实现的包装leveldb.net;不过leveldb.net只提供了基于byte[]和string的处理,这显然会对使用的时候带来不方便,毕竟在编写应用的时候都是希望通过对象的方式来存储,如我们常见的redis,mongodb和memcached等等都提供对象方式的读写.以下主要讲解leveldb.net基础上封装一层序列化功能方便使用.

制定对象化的访问接口

为了不修改leveldb.net的代码,所以选择在他基础过行封装,为了清楚需要些什么简单地定义了一个规则

public interface IDBManager
    {
        IFormater Formater { get; set; }

        void Set(string key, object data);

        object Get(string key, Type type);

        T Get<T>(string key);

        void Open();

        LevelDB.DB DataBase
        {
            get;
        }
       
    }
代码非常简单主要封装了GET,SET,实际上还有DELETE操作,这里偷懒就没做了:),为了提供灵活序列化规则所以在这个管理接口上还提供了一个Formater属性.下面是这相接口的详细实现:
public class LevelDBManager : IDBManager
    {

        public LevelDBManager()
        {
            
        }

        private LevelDB.DB mDataBase;

        public string Path { get; set; }

        public IFormater Formater
        {
            get;
            set;
        }

        public void Open()
        {
            mDataBase = new LevelDB.DB(Path, new Options() { CreateIfMissing = true });
          
        }

        public void Set(string key, object data)
        {

            FormaterBuffer buffer = Formater.Pop();
            try
            {

                int count = Formater.Serialize(data, buffer, 0);
                mDataBase.Put(Encoding.UTF8.GetBytes(key), buffer.Array, 0, count);
            }
            finally
            {
                Formater.Push(buffer);
            }
        }

        public object Get(string key, Type type)
        {
            FormaterBuffer buffer = Formater.Pop();
            long count;
            object result = null;
           
            try
            {
                count = mDataBase.Get(Encoding.UTF8.GetBytes(key), buffer.Array);
                if (count > 0)
                {
                    result = Formater.Deserialize(type, buffer, 0, (int)count);

                }
                return result;
            }
            finally
            {
                Formater.Push(buffer);
            }
          
        }

        public T Get<T>(string key)
        {
            return (T)Get(key, typeof(T));
        }


        public DB DataBase
        {
            get { return mDataBase; }
        }
    }
相信以上那些简知的代码也比较好理解,所以就不详细说明了.

可扩展的序列化规则

由于在使用上的需要,都习惯用些不同序列化方式来进行对象序列化,这个封装为了实现一个比较高的灵活度,所以对象序列化过程也制定了一个接口进行隔离.主要为了满足不同人的胃口.

public interface IFormater
    {
        FormaterBuffer Pop();

        void Push(FormaterBuffer data);

        int Serialize(object data, FormaterBuffer buffer, int offset);

        object Deserialize(Type type, FormaterBuffer buffer, int offset, int count);
    }
比较简单定义了序列化和反序列化的方法,不过为了一些性能上的考虑增加了buffer的复用功能,这个设计紧紧用作需要追求这方面性能要求而准备.下面看一下json和protobuf的实现是怎样的:
public abstract class FormaterBase:IFormater
    {
         private Stack<FormaterBuffer> mBufferPool = new Stack<FormaterBuffer>();

        const int BUFFER_SIZE = 1024 * 1024 * 1;

        public FormaterBase()
        {
            for (int i = 0; i < 20; i++)
            {
                mBufferPool.Push(new FormaterBuffer(BUFFER_SIZE));
            }
        }
        public FormaterBuffer Pop()
        {
            lock (mBufferPool)
            {
                if(mBufferPool.Count>0)
                    return mBufferPool.Pop();
                return new FormaterBuffer(BUFFER_SIZE);
            }
        }
        public void Push(FormaterBuffer data)
        {
            lock (mBufferPool)
            {
                mBufferPool.Push(data);
            }
        }
       
        public abstract int Serialize(object data, FormaterBuffer buffer, int offset);
       
        public abstract object Deserialize(Type type, FormaterBuffer buffer, int offset, int count);
        
    }
  • json
    public class JsnoFormater:FormaterBase
        {
           
            public int Serialize(object data, byte[] buffer, int offset)
            {
                string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
                return Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, offset);
            }
    
            public override int Serialize(object data, FormaterBuffer buffer, int offset)
            {
                string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
                return Encoding.UTF8.GetBytes(json, 0, json.Length, buffer.Array, offset);
            }
    
            public override object Deserialize(Type type, FormaterBuffer buffer, int offset, int count)
            {
                string value = Encoding.UTF8.GetString(buffer.Array, offset, count);
                return Newtonsoft.Json.JsonConvert.DeserializeObject(value, type);
            }
        }
  • protobuf
    public class ProtobufFormater:FormaterBase
        {
    
            public override int Serialize(object data, FormaterBuffer buffer, int offset)
            {
                buffer.Seek(offset);
                ProtoBuf.Meta.RuntimeTypeModel.Default.Serialize(buffer.Stream, data);
                return (int)buffer.Stream.Position;
            }
    
            public override object Deserialize(Type type, FormaterBuffer buffer, int offset, int count)
            {
                buffer.Stream.SetLength(count + offset);
                buffer.Seek(offset);
                return ProtoBuf.Meta.RuntimeTypeModel.Default.Deserialize(buffer.Stream, null, type);
            }
        }

leveldb.net的一些简单性能改造

虽然leveldb.net只以win dll的基础上包装,但在包装过程的确有些方法针对我个人来说做得并不理想,主要体现在buffer复用方面.其实get,set方法都存在这情况.

/// <summary>
        /// Set the database entry for "key" to "value".  
        /// </summary>
        public void Put(byte[] key, byte[] value, WriteOptions options)
        {
            IntPtr error;
            LevelDBInterop.leveldb_put(this.Handle, options.Handle, key, (IntPtr)key.Length, value, (IntPtr)value.LongLength, out error);
            LevelDBException.Check(error);
            GC.KeepAlive(options);
            GC.KeepAlive(this);
        }
public unsafe byte[] Get(byte[] key, ReadOptions options)
        {
            IntPtr error;
            IntPtr lengthPtr;
            var valuePtr = LevelDBInterop.leveldb_get(this.Handle, options.Handle, key, (IntPtr)key.Length, out lengthPtr, out error);
            LevelDBException.Check(error);
            if (valuePtr == IntPtr.Zero)
                return null;
            try
            {
                var length = (long)lengthPtr;
                var value = new byte[length];
                var valueNative = (byte*)valuePtr.ToPointer();
                for (long i = 0; i < length; ++i)
                    value[i] = valueNative[i];
                return value;
            }
            finally
            {
                LevelDBInterop.leveldb_free(valuePtr);
                GC.KeepAlive(options);
                GC.KeepAlive(this);
            }
        }
两上个方法都不支持从外部带入buffer的情况,当需要高并发操作的情况而对象序列化内容又比较大的情况下,那的确是会让人感觉到不满意.所以在这基础上添加了一些有利于buffer复用的方法来支持高并发操作下的性能需要.
public void Put(byte[] key, byte[] value, int offset, int length, WriteOptions options)
        {
            IntPtr error;
            LevelDBInterop.leveldb_put(this.Handle, options.Handle, key, (IntPtr)key.Length, value, (IntPtr)length, out error);
            LevelDBException.Check(error);
            GC.KeepAlive(options);
            GC.KeepAlive(this);
        }
public unsafe long Get(byte[] key, byte[] buffer, ReadOptions options)
        {
            IntPtr error;
            IntPtr lengthPtr;
            var valuePtr = LevelDBInterop.leveldb_get(this.Handle, options.Handle, key, (IntPtr)key.Length, out lengthPtr, out error);
            LevelDBException.Check(error);
            if (valuePtr == IntPtr.Zero)
                return 0;
            try
            {
                var length = (long)lengthPtr;
                var valueNative = (byte*)valuePtr.ToPointer();
                Marshal.Copy((IntPtr)valuePtr, buffer, 0, (int)length);
                return length;
            }
            finally
            {
                LevelDBInterop.leveldb_free(valuePtr);
              
            }
        }

*--------------------------------------
*个人站:www.ikende.com www.asquestion.com              
*个人开源项目github.com/IKende             
*--------------------------------------*

© 著作权归作者所有

泥水佬

泥水佬

粉丝 77
博文 95
码字总数 65026
作品 7
广州
架构师
私信 提问
LevelDB.NET性能测试

最近了解了一下LevelDB,发觉这个嵌入式的K-V数据性能不错,所以顺便想在使用层面了解一下.由于LevelDB也有针对.net的实现,所以就针对了LevelDB.NET进行了一个简单的读写压力测试.为了更客观的...

泥水佬
2014/08/20
999
2
LevelDB.NET

LevelDB.NET 是 LevelDB 在 windows下 .net 调用,代码 C# 完成 .NET DLL is AnyCPU, but it automatically forwards all calls to one of the two native DLLs depending on whether the c......

kstrain
2014/09/24
1K
0
Android持久层框架--Androrm

Androrm 是 Android 平台上的一个对象关系映射框架,也就是我们常说的 ORM 框架。用于帮助你快速开发使用数据库的应用,封装了常用的表创建、对象读写和查询,通过将 Model 类映射到数据库表...

匿名
2011/07/24
2.5K
0
java IO流

IO(Input Output)流 IO流用来处理设备之间的数据传输,对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中。 完整IO流结构图如下: IO流分类 1. 处理的数据单位不同: (1)字符流 ...

iborder
2016/09/05
68
0
【Qt笔记】使用 QJsonDocument 处理 JSON

上一章我们了解了如何使用 QJson 处理 JSON 文档。QJson 是一个基于 Qt 的第三方库,适用于 Qt4 和 Qt5 两个版本。不过,如果你的应用仅仅需要考虑兼容 Qt5,其实已经有了内置的处理函数。Q...

大道无名
2016/08/05
721
0

没有更多内容

加载失败,请刷新页面

加载更多

谁说多功能和低价格不能兼得?Aspose系列产品1024购买指南请查收!

你还在为了Word、Excel、PDF、CAD等文档格式转换而发愁吗? 你是否在寻找一款能够在应用程序中文档管理的工具呢? Aspose——支持100多种文件格式创建、编辑、转换和打印! 往下看,找一找哪...

mnrssj
20分钟前
3
0
hbase客户端API

本章介绍用于对HBase表上执行CRUD操作的HBase Java客户端API。 HBase是用Java编写的,并具有Java原生API。因此,它提供了编程访问数据操纵语言(DML)。 HBaseConfiguration类 添加 HBase 的配...

水木星辰
21分钟前
3
0
[插件化开发] 1. 初识OSGI

初识 OSGI 背景 当前product是以solution的方式进行售卖,但是随着公司业务规模的快速夸张,随之而来的是新客户的产品开发,老客户的产品维护,升级以及修改bug,团队的效能明显下降,为了解...

IsaacZhang
21分钟前
4
0
Webstorm 环境使用 nuxt.js 做开发,@ 和 ~ 别名配置

好的IDE + 好的代码提示 = 高效率的开发 webstorm 设置@和~别名,有助于代码查看和跳转. step 0 在项目下创建一个webpack.config.js,内容如下: const path = require('path')module.exp...

皇虫
25分钟前
3
0
Knative 实战:基于 Knative Serverless 技术实现天气服务-下篇

上一期我们介绍了如何基于 Knative Serverless 技术实现天气服务-上篇,首先我们先来回顾一下上篇介绍的内容: 通过高德天气 API 接口,每隔 3 个小时定时发送定时事件,将国内城市未来 3 天...

Mr_zebra
43分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部