Berkeley DB是历史悠久的嵌入式数据库系统,主要应用在UNIX/LINUX操作系统上。Berkeley DB的存储的是key/value键值对,可以理解为硬盘上的超级hash表。其可以管理256TB数据,而且能支撑几千个并发访问。目前Berkeley DB有C++版和Java版。所以,我们需要一个访问的中间转换,已经有人发布了C#的API。可以从 Berkeley DB for .NET 上面找到,现在最新版是0.95版本,可以支持4.3和4.5版。本篇将以4.5版做实例。BerkeleyDB的版本可以在http://www.oracle.com/technology/products/berkeley-db/index.html下载,当前最新版本为4.7版。4.5 C++版的Berkeley DB可以在http://www.oracle.com/technology/software/products/berkeley-db/db/index.html这里下载。 By Birdshover@ 博客园 http://www.cnblogs.com/birdshover/ 下载到Berkeley DB for .Net的API——libdb-dotnet_0_95.zip后,就可以开始使用了。首先在libdb-dotnet_0_95.zip解压缩的bin目录找到libdb_dotNET45.dll,这个就是4.5版本使用的dll。新建项目,引用这个dll。注意,自己编译源码可能会编译不过,主要是因为里面一些委托和委托的参数可见性不一致造成的。把那些参数用到的class 或者struct都调成public即可。 BerkeleyDB的数据库操作需要借助DbBTree类。因此需要先得到DbBTree的实例,但是DbBTree类会对其它几个类有依赖,必须依赖其它几个类才能创建。 下面代码就是初始化得到DbBTree实例的一个过程。 /// <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 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); } 另外Berkeley DB数据库的操作需要借助于序列化。 /// <summary> /// 二进制序列化 /// </summary> private BinaryFormatter formatter; /// <summary> /// 键内存流 /// </summary> private MemoryStream keyStream; /// <summary> /// 内容内存流 /// </summary> private MemoryStream dataStream; private void StreamInit() { formatter = new BinaryFormatter(); keyStream = new MemoryStream(); dataStream = new MemoryStream(); } Berkeley DB是键值数据库,因此定义一个获取键接口: public interface IPut { string Key { get; } } 一、数据库的保存与更新 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; } } 上述代码就可以保存键值。显示对键值进行序列化,然后再保存。保存完有三个状态,可以一一处理。 二、数据库的删除 删除最为简单 public bool Remove(IPut put) { 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; } } 三、关于添加和删除 添加和删除并没有真正得进行添加和删除。必须执行Commit操作: private bool iscomit = false; public void Commit() { txn.Commit(Txn.CommitMode.None); iscomit = true; } 四、寻找键 用键查询值,和hash表一样使用。 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 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; } 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); } 六、完整操作封装 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); } } 调用方法: 首先要有一个写入的实体类,必须可以序列化,并且实现IPut接口: [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();
|