leveldb.Netオブジェクト読み書きパッケージ

21318 ワード

leveldbは非常に効率的な埋め込み可能なK-Vデータベースである.NETの下にはwinベースのパッケージleveldbがある.net;だがNetはbyte[]とstringに基づく処理のみを提供しており、これは明らかに使用時に不便をもたらす.結局、アプリケーションを作成する際には、一般的なredis、mongodb、memcachedなど、オブジェクト方式の読み書きを提供することを望んでいるからだ.以下では主にleveldbについて説明する.Netに基づいて1層のシーケンス化機能をカプセル化することは便利である.
オブジェクト化されたアクセスインタフェースの作成
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操作もあり、ここではサボっていません:)、柔軟なシーケンス化ルールを提供するために、この管理インタフェースにはFormat属性も提供しています.次に、このインタフェースの詳細な実装を示します.
 
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のいくつかの簡単な性能の改造
    だが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);
                  
                }
            }