文章分类

站点统计

  • 分类总数: 13 个
  • 文章总数: 145 篇
  • 评论总数: 47 条
  • 附件总数: 59 个
  • 建站日期: 2008-08-18
  • 访问总数: 472869 人次
  • RSS订阅: 文章|评论

在.NET中使用Berkeley DB

Admin 于 2008-10-12 10:54:57 发表.Net

订阅: http://www.kaiyuan8.org/Feed/Article_115.aspx
引用: 点这里获取地址 (UTF-8)
Mono 2.0发布:介绍及特性列表 < 在.NET中使用Berkeley DB > Access、HSQLDB、Firebird、Sybase、Derby、SQLite、MySQL及DB4O客户端Embedded数据库比较

Berkeley DB是历史悠久的嵌入式数据库系统,早期主要应用在UNIX/LINUX操作系统上,现在也有大量的Windows应用程序使用Berkeley DB存储数据。Berkeley DB的存储的是key/value键值对,可以理解为硬盘上的超级hash表,可以管理256TB数据,而且能支撑几千个并发访问。BerkeleyDB相关资料可以从这里下载:http://www.oracle.com/technology/products/berkeley-db/index.html
    由于SQLite只支持单写多读,大多数情况下无法满足大访问量Web站点应用的并发性要求;而DB4O的Embed模式完全不支持并发;可以考虑使用Berkeley DB作为Asp.Net的Embed数据库。

目前Berkeley DB官方有C++版和Java版。要想在.NET中使用,可以使用Berkeley DB for .NET的Binding(Wrapper)。Berkeley DB for .NET 最新版本为0.95,支持BDB 4.3和4.5。下载到libdb-dotnet_0_95.zip后,解压,在解压缩后的bin目录找到libdb_dotNET45.dll,这就是BDB 4.5的.NET Binding,在.NET项目中引用此DLL就可以开始使用了。

一、初始化DbBTree实例
    ①、Berkeley DB通过DbBTree类进行数据库操作,因此需要先得到DbBTree的实例。

  1. /// <summary> 
  2. /// 数据库目录 
  3. /// </summary> 
  4. private string directory; 
  5. /// <summary> 
  6. /// 数据库文件名 
  7. /// </summary> 
  8. private string dbName; 
  9.  
  10. private DbBTree btree; 
  11. private Txn txn; 
  12. private Db db; 
  13. private Env env; 
  14. /// <summary> 
  15. /// 初始化 
  16. /// </summary> 
  17. private void Init() 
  18.     env = new Env(EnvCreateFlags.None); 
  19.     Env.OpenFlags envFlags = 
  20.       Env.OpenFlags.Create | 
  21.       Env.OpenFlags.InitLock | 
  22.       Env.OpenFlags.InitLog | 
  23.       Env.OpenFlags.InitMPool | 
  24.       Env.OpenFlags.InitTxn | 
  25.       Env.OpenFlags.Recover; 
  26.     env.Open(directory, envFlags, 0); 
  27.     txn = env.TxnBegin(null, Txn.BeginFlags.None); 
  28.     db = env.CreateDatabase(DbCreateFlags.None); 
  29.     btree = (DbBTree)db.Open(txn, dbName, null, DbType.BTree, Db.OpenFlags.Create, 0); 

    ②、另外Berkeley DB数据库的操作需要借助于序列化。

  1. /// <summary> 
  2. /// 二进制序列化 
  3. /// </summary> 
  4. private BinaryFormatter formatter; 
  5. /// <summary> 
  6. /// 键内存流 
  7. /// </summary> 
  8. private MemoryStream keyStream; 
  9. /// <summary> 
  10. /// 内容内存流 
  11. /// </summary> 
  12. private MemoryStream dataStream; 
  13.  
  14. private void StreamInit() 
  15.     formatter = new BinaryFormatter(); 
  16.     keyStream = new MemoryStream(); 
  17.     dataStream = new MemoryStream(); 

    ③、Berkeley DB是键值数据库,因此定义一个获取键接口:

  1. public interface IPut 
  2.     string Key { get; } 

二、数据库的保存与更新

  1. public bool Set(IPut put) 
  2.     Reset(); 
  3.  
  4.     keyStream.Position = 0; 
  5.     formatter.Serialize(keyStream, put.Key); 
  6.     DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position); 
  7.     dataStream.Position = 0; 
  8.     formatter.Serialize(dataStream, put); 
  9.     DbEntry data = DbEntry.InOut(dataStream.GetBuffer(), 0, (int)dataStream.Position); 
  10.     WriteStatus status = btree.Put(txn, ref key, ref data); 
  11.     switch (status) 
  12.     { 
  13.         case WriteStatus.Success: 
  14.             return true
  15.         case WriteStatus.NotFound: 
  16.         case WriteStatus.KeyExist: 
  17.         default
  18.             return false
  19.     } 

上述代码就可以保存键值,对键值进行序列化,然后再保存。保存完有三个状态,可以一一处理。
三、数据库的删除

  1. public bool Remove(IPut put) 
  2.     keyStream.Position = 0; 
  3.     formatter.Serialize(keyStream, put.Key); 
  4.     DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position); 
  5.     DeleteStatus status = btree.Delete(txn, ref key); 
  6.     switch (status) 
  7.     { 
  8.         case DeleteStatus.NotFound: 
  9.         case DeleteStatus.Success: 
  10.             return true
  11.         case DeleteStatus.KeyEmpty: 
  12.         default
  13.             return false
  14.     } 

四、添加和删除并没有真正得进行添加和删除,须执行Commit操作:

  1. private bool iscomit = false
  2. public void Commit() 
  3.     txn.Commit(Txn.CommitMode.None); 
  4.     iscomit = true

五、用键查询值,和hash表一样使用:

  1. public bool Get(ref IPut put) 
  2.     keyStream.Position = 0; 
  3.     formatter.Serialize(keyStream, put.Key); 
  4.     DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position); 
  5.     dataStream.SetLength(dataStream.Capacity); 
  6.     DbEntry data = DbEntry.Out(dataStream.GetBuffer()); 
  7.  
  8.     while (true
  9.     { 
  10.         ReadStatus status = btree.Get(txn, ref key, ref data, DbFile.ReadFlags.None); 
  11.  
  12.         switch (status) 
  13.         { 
  14.             case ReadStatus.Success: 
  15.                 dataStream.Position = 0; 
  16.                 dataStream.SetLength(data.Size); 
  17.                 put = (IPut)formatter.Deserialize(dataStream); 
  18.                 return true
  19.             case ReadStatus.BufferSmall: //扩容 
  20.                 if (key.Buffer.Length < key.Size) 
  21.                 { 
  22.                     keyStream.SetLength(key.Size); 
  23.                     key = DbEntry.Out(keyStream.GetBuffer()); 
  24.                 } 
  25.                 if (data.Buffer.Length < data.Size) 
  26.                 { 
  27.                     dataStream.SetLength(data.Size); 
  28.                     data = DbEntry.Out(dataStream.GetBuffer()); 
  29.                 } 
  30.                 continue
  31.             case ReadStatus.NotFound: 
  32.             case ReadStatus.KeyEmpty: 
  33.             default
  34.                 return false
  35.         } 
  36.     } 

六、遍历

  1. public List<IPut> Find() 
  2.     List<IPut> custList = new List<IPut>(); 
  3.     using (DbBTreeCursor cursor = btree.OpenCursor(txn, DbFileCursor.CreateFlags.None)) 
  4.     { 
  5.         IPut cust = null
  6.         while (GetNextRecord(cursor, ref cust)) 
  7.             custList.Add(cust); 
  8.     } 
  9.     return custList; 
  10.  
  11. private bool GetNextRecord(DbBTreeCursor cursor, ref IPut cust) 
  12.     ReadStatus status; 
  13.     keyStream.SetLength(keyStream.Capacity); 
  14.     dataStream.SetLength(dataStream.Capacity); 
  15.     DbEntry key = DbEntry.Out(keyStream.GetBuffer()); 
  16.     DbEntry data = DbEntry.Out(dataStream.GetBuffer()); 
  17.     do 
  18.     { 
  19.         status = cursor.Get(ref key, ref data, DbFileCursor.GetMode.Next, DbFileCursor.ReadFlags.None); 
  20.         switch (status) 
  21.         { 
  22.             case ReadStatus.NotFound: return false
  23.             case ReadStatus.KeyEmpty: continue;  // skip deleted records 
  24.             case ReadStatus.BufferSmall: 
  25.                 if (key.Buffer.Length < key.Size) 
  26.                 { 
  27.                     keyStream.SetLength(key.Size); 
  28.                     key = DbEntry.Out(keyStream.GetBuffer()); 
  29.                 } 
  30.                 if (data.Buffer.Length < data.Size) 
  31.                 { 
  32.                     dataStream.SetLength(data.Size); 
  33.                     data = DbEntry.Out(dataStream.GetBuffer()); 
  34.                 } 
  35.                 continue
  36.             case ReadStatus.Success: 
  37.                 dataStream.Position = 0; 
  38.                 dataStream.SetLength(data.Size); 
  39.                 cust = (IPut)formatter.Deserialize(dataStream); 
  40.                 return true
  41.             default
  42.                 return false
  43.         } 
  44.     } while (true); 

七、完整操作封装


public interface IPut
{
string Key { get; }
}

public class BDBManager : IDisposable
{
/// <summary>
/// 数据库目录
/// </summary>
private string directory;
/// <summary>
/// 数据库文件名
/// </summary>
private string dbName;

private DbBTree btree;
private Txn txn;
private Db db;
private Env env;

/// <summary>
/// 二进制序列化
/// </summary>
private BinaryFormatter formatter;
/// <summary>
/// 键内存流
/// </summary>
private MemoryStream keyStream;
/// <summary>
/// 内容内存流
/// </summary>
private MemoryStream dataStream;


public BDBManager(string directory, string dbName)
{
    this.directory = directory;
    this.dbName = dbName;

    Init();
    StreamInit();
}

public bool Set(IPut put)
{
    Reset();

    keyStream.Position = 0;
    formatter.Serialize(keyStream, put.Key);
    DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position);
    dataStream.Position = 0;
    formatter.Serialize(dataStream, put);
    DbEntry data = DbEntry.InOut(dataStream.GetBuffer(), 0, (int)dataStream.Position);
    WriteStatus status = btree.Put(txn, ref key, ref data);
    switch (status)
    {
        case WriteStatus.Success:
            return true;
        case WriteStatus.NotFound:
        case WriteStatus.KeyExist:
        default:
            return false;
    }
}

private bool iscomit = false;
public void Commit()
{
    txn.Commit(Txn.CommitMode.None);
    iscomit = true;
}

public List<IPut> Find()
{
    List<IPut> custList = new List<IPut>();
    using (DbBTreeCursor cursor = btree.OpenCursor(txn, DbFileCursor.CreateFlags.None))
    {
        IPut cust = null;
        while (GetNextRecord(cursor, ref cust))
            custList.Add(cust);
    }
    return custList;
}

public bool Get(ref IPut put)
{
    keyStream.Position = 0;
    formatter.Serialize(keyStream, put.Key);
    DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position);
    dataStream.SetLength(dataStream.Capacity);
    DbEntry data = DbEntry.Out(dataStream.GetBuffer());

    while (true)
    {
        ReadStatus status = btree.Get(txn, ref key, ref data, DbFile.ReadFlags.None);

        switch (status)
        {
            case ReadStatus.Success:
                dataStream.Position = 0;
                dataStream.SetLength(data.Size);
                put = (IPut)formatter.Deserialize(dataStream);
                return true;
            case ReadStatus.BufferSmall: //扩容
                if (key.Buffer.Length < key.Size)
                {
                    keyStream.SetLength(key.Size);
                    key = DbEntry.Out(keyStream.GetBuffer());
                }
                if (data.Buffer.Length < data.Size)
                {
                    dataStream.SetLength(data.Size);
                    data = DbEntry.Out(dataStream.GetBuffer());
                }
                continue;
            case ReadStatus.NotFound:
            case ReadStatus.KeyEmpty:
            default:
                return false;
        }
    }
}

public bool Remove(IPut put)
{
    Reset();

    keyStream.Position = 0;
    formatter.Serialize(keyStream, put.Key);
    DbEntry key = DbEntry.InOut(keyStream.GetBuffer(), 0, (int)keyStream.Position);
    DeleteStatus status = btree.Delete(txn, ref key);
    switch (status)
    {
        case DeleteStatus.NotFound:
        case DeleteStatus.Success:
            return true;
        case DeleteStatus.KeyEmpty:
        default:
            return false;
    }
}

public void Dispose()
{
    if (!iscomit)
        Commit();
    db.Close();
    db.Close();
}

private void Reset()
{
    iscomit = false;
}

private void Init()
{
    env = new Env(EnvCreateFlags.None);
    Env.OpenFlags envFlags =
      Env.OpenFlags.Create |
      Env.OpenFlags.InitLock |
      Env.OpenFlags.InitLog |
      Env.OpenFlags.InitMPool |
      Env.OpenFlags.InitTxn |
      Env.OpenFlags.Recover;
    env.Open(directory, envFlags, 0);
    txn = env.TxnBegin(null, Txn.BeginFlags.None);
    db = env.CreateDatabase(DbCreateFlags.None);
    btree = (DbBTree)db.Open(txn, dbName, null, DbType.BTree, Db.OpenFlags.Create, 0);
}

private void StreamInit()
{
    formatter = new BinaryFormatter();
    keyStream = new MemoryStream();
    dataStream = new MemoryStream();
}

private bool GetNextRecord(DbBTreeCursor cursor, ref IPut cust)
{
    ReadStatus status;
    keyStream.SetLength(keyStream.Capacity);
    dataStream.SetLength(dataStream.Capacity);
    DbEntry key = DbEntry.Out(keyStream.GetBuffer());
    DbEntry data = DbEntry.Out(dataStream.GetBuffer());
    do
    {
        status = cursor.Get(ref key, ref data, DbFileCursor.GetMode.Next, DbFileCursor.ReadFlags.None);
        switch (status)
        {
            case ReadStatus.NotFound: return false;
            case ReadStatus.KeyEmpty: continue;  // skip deleted records
            case ReadStatus.BufferSmall:
                if (key.Buffer.Length < key.Size)
                {
                    keyStream.SetLength(key.Size);
                    key = DbEntry.Out(keyStream.GetBuffer());
                }
                if (data.Buffer.Length < data.Size)
                {
                    dataStream.SetLength(data.Size);
                    data = DbEntry.Out(dataStream.GetBuffer());
                }
                continue;
            case ReadStatus.Success:
                dataStream.Position = 0;
                dataStream.SetLength(data.Size);
                cust = (IPut)formatter.Deserialize(dataStream);
                return true;
            default:
                return false;
        }
    } while (true);
}
}
调用方法:
[Serializable()]
class Item : IPut
{
    public string Name { get; set; }
    public string Text { get; set; }
    public int ID { get; set; }

    public override string ToString()
    {
        return string.Format("ID:{0} Key:{1}", ID, Name);
    }

    public string Key
    {
        get { return Name; }
    }
}
using (BDBManager manager = new BDBManager("db", "db.dat"))
{
    bool success = manager.Set(new Item() { ID = 1000, Name = "Test",Text = "213" });
    Console.WriteLine(string.Format("set is {0}", success));
}

using (BDBManager manager = new BDBManager("db", "db.dat"))
{
    IPut put =  new Item() { Name = "Test" };
    bool success = manager.Get(ref put);
    Console.WriteLine(string.Format("read is {0},item : {1}", success, put.ToString()));
}

using (BDBManager manager = new BDBManager("db", "db.dat"))
{
    IPut put = new Item() { Name = "Test" };
    bool success = manager.Remove(put);
    Console.WriteLine(string.Format("remove is {0},item : {1}", success, put.ToString()));
}

using (BDBManager manager = new BDBManager("db", "db.dat"))
{
    List<IPut> list = manager.Find();
    foreach (var item in list)
    {
        Console.WriteLine(item.ToString());
    }
}
Console.WriteLine("end");
Console.ReadKey();

被阅1214次, 0投一票Berkeley
  • 看完了要说点啥么?
  • 昵称 (不填说不了话)
  • 信箱地址 (不会被公开,但是不填也说不了话)
  • 网址 (这个不填也成)
Powered by MiniBoke v2.0.0.8 Build 0828

Copyright © 2008 开源吧!. All rights reserved.

粤ICP备07500939号