首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 整合 Berkeley DB 嵌入式鍵值存儲:從初始化到 CRUD 完整封裝實戰

     編輯:關於C#

Berkeley DB 簡介

Oracle Berkeley DB 是高效能、支援交易的嵌入式鍵值資料庫(B+ 樹/哈希/隊列/遞迴樹)。.NET 透過 libdb_dotNET45.dll(BDB 4.5/4.7)互操作。核心優勢:零配置、ACID 交易、單機 256TB、千級並發。

核心類別與初始化流程


using BerkeleyDB; // 引用 libdb_dotNET45.dll

public class BdbManager : IDisposable {
    private readonly string _dir, _dbName;
    private Env _env;
    private Db _db;
    private DbBTree _btree;
    private Txn _txn;
    private readonly BinaryFormatter _formatter = new BinaryFormatter();
    private readonly MemoryStream _keyStream = new MemoryStream();
    private readonly MemoryStream _dataStream = new MemoryStream();
    private bool _committed;

    public BdbManager(string directory, string dbName) {
        _dir = directory; _dbName = dbName;
        Initialize();
    }

    private void Initialize() {
        _env = new Env(EnvCreateFlags.None);
        var flags = Env.OpenFlags.Create | Env.OpenFlags.InitLock | Env.OpenFlags.InitLog
                  | Env.OpenFlags.InitMPool | Env.OpenFlags.InitTxn | Env.OpenFlags.Recover;
        _env.Open(_dir, flags, 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 ResetStreams() {
    _keyStream.Position = 0; _keyStream.SetLength(0);
    _dataStream.Position = 0; _dataStream.SetLength(0);
}

private DbEntry SerializeKey(TKey key) {
    _keyStream.Position = 0;
    _formatter.Serialize(_keyStream, key);
    return DbEntry.InOut(_keyStream.GetBuffer(), 0, (int)_keyStream.Position);
}

private DbEntry SerializeValue(TVal val) {
    _dataStream.Position = 0;
    _formatter.Serialize(_dataStream, val);
    return DbEntry.InOut(_dataStream.GetBuffer(), 0, (int)_dataStream.Position);
}

private TVal DeserializeValue(DbEntry entry) {
    _dataStream.Position = 0;
    _dataStream.SetLength(entry.Size);
    return (TVal)_formatter.Deserialize(_dataStream);
}

CRUD 操作封裝

儲存/更新


public bool Put(TKey key, TVal value) where TVal : class {
    ResetStreams();
    var k = SerializeKey(key);
    var v = SerializeValue(value);
    var status = _btree.Put(_txn, ref k, ref v);
    return status == WriteStatus.Success;
}

讀取


public bool Get(TKey key, out TVal value) where TVal : class {
    var k = SerializeKey(key);
    var v = DbEntry.Out(_dataStream.GetBuffer()); // 初始緩衝區
    while (true) {
        var status = _btree.Get(_txn, ref k, ref v, DbFile.ReadFlags.None);
        switch (status) {
            case ReadStatus.Success:
                value = DeserializeValue(v);
                return true;
            case ReadStatus.BufferSmall:
                // 動態擴容
                if (k.Buffer.Length < k.Size) {
                    _keyStream.SetLength(k.Size);
                    k = DbEntry.Out(_keyStream.GetBuffer());
                }
                if (v.Buffer.Length < v.Size) {
                    _dataStream.SetLength(v.Size);
                    v = DbEntry.Out(_dataStream.GetBuffer());
                }
                continue;
            default:
                value = default;
                return false;
        }
    }
}

刪除


public bool Delete(TKey key) {
    var k = SerializeKey(key);
    var status = _btree.Delete(_txn, ref k);
    return status == DeleteStatus.Success || status == DeleteStatus.NotFound;
}

交易提交與資源釋放


public void Commit() {
    _txn.Commit(Txn.CommitMode.None);
    _committed = true;
}

public void Dispose() {
    if (!_committed) Commit();
    _db?.Close();
    _env?.Close();
}

游標遍歷全表


public IEnumerable Scan() where TVal : class {
    using (var cursor = _btree.OpenCursor(_txn, DbFileCursor.CreateFlags.None)) {
        var k = DbEntry.Out(_keyStream.GetBuffer());
        var v = DbEntry.Out(_dataStream.GetBuffer());
        while (true) {
            var status = cursor.Get(ref k, ref v, DbFileCursor.GetMode.Next, DbFileCursor.ReadFlags.None);
            if (status == ReadStatus.NotFound) yield break;
            if (status == ReadStatus.KeyEmpty) continue; // 跳過已刪除
            if (status == ReadStatus.BufferSmall) { /* 擴容邏輯同 Get */ continue; }
            if (status == ReadStatus.Success) {
                _dataStream.Position = 0;
                _dataStream.SetLength(v.Size);
                yield return (TVal)_formatter.Deserialize(_dataStream);
            }
        }
    }
}

使用範例


[Serializable]
public class Item : IKeyProvider {
    public string Name { get; set; } // 作為 Key
    public string Text { get; set; }
    public int Id { get; set; }
    public string Key => Name;
}

using (var mgr = new BdbManager("db", "items.dat")) {
    mgr.Put("key1", new Item { Id = 1, Name = "key1", Text = "Hello" });
    mgr.Commit();
}

using (var mgr = new BdbManager("db", "items.dat")) {
    if (mgr.Get("key1", out Item item)) Console.WriteLine(item.Text);
    foreach (var it in mgr.Scan()) Console.WriteLine($"{it.Key}: {it.Text}");
}

關鍵注意事項

  • 序列化相容性:BinaryFormatter 版本敏感,生產環境建議改用 protobuf-net、MessagePack 或 System.Text.Json(需自訂 DbEntry 序列化)。
  • 交易粒度:批量寫入時單一交易提交,效能遠優於逐筆提交。
  • 環境目錄:Env.Open 目錄需寫入權限,包含日誌、鎖、緩存檔案。
  • 並發:InitLock + InitTxn 支援多執行緒/進程並發,鎖衝突會拋出 DeadlockException 需重試。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved