LiteDB 并發控制與多線程訪問深度解析
并發控制概述
在現代軟件開發中,并發控制是確保數據一致性和完整性的關鍵技術。對于輕量級嵌入式數據庫 LiteDB 來說,有效的并發控制機制尤為重要。本文將詳細探討 LiteDB 中的并發控制策略、多線程訪問模式以及在多設備數據同步中的應用。
Nuget 安裝LiteDB
圖片
LiteDB 并發控制基礎
鎖機制原理
LiteDB 提供了多種鎖定機制來管理并發訪問:
using LiteDB;
namespace App13
{
publicclass User
{
publicstring Name { get; set; }
publicint Age { get; set; }
}
internal class Program
{
// 數據庫實例
privatestatic LiteDatabase _database;
// 創建一個靜態對象作為鎖對象
privatestatic readonly object _lock = new object();
static void Main(string[] args)
{
ExclusiveLockExample();
SharedLockExample();
}
// 共享鎖:允許多個讀取操作同時進行
public static void SharedLockExample()
{
using (var db = new LiteDatabase(@"MyData.db"))
{
// 使用共享鎖進行讀取操作
var collection = db.GetCollection<User>("users");
// 多線程并發讀取不會相互阻塞
Parallel.For(0, 10, i =>
{
var users = collection.Find(u => u.Age > 18);
Console.WriteLine($"Thread {i} read {users.Count()} users");
});
}
}
// 排他鎖:確保寫入操作的原子性
public static void ExclusiveLockExample()
{
using (var db = new LiteDatabase(@"MyData.db"))
{
var collection = db.GetCollection<User>("users");
// 使用靜態鎖對象替代 this
lock (_lock)
{
// 寫入操作
var newUser = new User
{
Name = "張三",
Age = 30
};
collection.Insert(newUser);
}
}
}
}
}
圖片
多線程訪問模式
讀-寫并發控制
using LiteDB;
namespace App13
{
// 產品模型類
publicclass Product
{
public ObjectId Id { get; set; }
publicstring Name { get; set; }
public decimal Price { get; set; }
publicint Stock { get; set; }
public DateTime CreateTime { get; set; }
}
publicclass MultiThreadAccess : IDisposable
{
private readonly object _lockObject = new object();
privateconststring DbPath = @"MyData.db";
private readonly ConnectionString _connectionString;
public MultiThreadAccess()
{
// 配置連接字符串,啟用文件共享
_connectionString = new ConnectionString
{
Filename = DbPath,
Connection = ConnectionType.Shared // 使用共享連接模式
};
// 初始化數據庫
InitializeDatabase();
}
private void InitializeDatabase()
{
using (var db = new LiteDatabase(_connectionString))
{
var collection = db.GetCollection<Product>("products");
// 如果集合為空,添加測試數據
if (!collection.Find(Query.All()).Any())
{
var products = new List<Product>
{
new Product
{
Name = "舊產品",
Price = 150.00m,
Stock = 10,
CreateTime = DateTime.Now.AddDays(-10)
},
new Product
{
Name = "常規產品",
Price = 99.99m,
Stock = 20,
CreateTime = DateTime.Now.AddDays(-5)
}
};
collection.InsertBulk(products);
}
}
}
public void SafeConcurrentAccess()
{
try
{
// 為每個操作創建單獨的數據庫連接
Parallel.Invoke(
() => ReadProducts(),
() => WriteProducts(),
() => UpdateProducts(),
() => DeleteProducts(),
() => QueryProducts()
);
}
catch (Exception ex)
{
Console.WriteLine($"并發操作出錯: {ex.Message}");
}
}
private void ReadProducts()
{
using (var db = new LiteDatabase(_connectionString))
{
try
{
var collection = db.GetCollection<Product>("products");
var products = collection.Find(p => p.Price > 100);
Console.WriteLine($"讀取到 {products.Count()} 個高價產品");
foreach (var product in products)
{
Console.WriteLine($"產品: {product.Name}, 價格: {product.Price:C}");
}
}
catch (Exception ex)
{
Console.WriteLine($"讀取操作失敗: {ex.Message}");
}
}
}
private void WriteProducts()
{
using (var db = new LiteDatabase(_connectionString))
{
lock (_lockObject)
{
try
{
var collection = db.GetCollection<Product>("products");
var newProduct = new Product
{
Name = $"新產品_{DateTime.Now.Ticks}",
Price = 199.99m,
Stock = 5,
CreateTime = DateTime.Now
};
collection.Insert(newProduct);
Console.WriteLine($"成功添加新產品: {newProduct.Name}");
}
catch (Exception ex)
{
Console.WriteLine($"寫入操作失敗: {ex.Message}");
}
}
}
}
private void UpdateProducts()
{
using (var db = new LiteDatabase(_connectionString))
{
lock (_lockObject)
{
try
{
var collection = db.GetCollection<Product>("products");
var product = collection.FindOne(p => p.Name == "舊產品");
if (product != null)
{
product.Price *= 1.1m;
product.Stock -= 1;
collection.Update(product);
Console.WriteLine($"更新產品價格: {product.Name} 新價格: {product.Price:C}");
}
}
catch (Exception ex)
{
Console.WriteLine($"更新操作失敗: {ex.Message}");
}
}
}
}
private void DeleteProducts()
{
using (var db = new LiteDatabase(_connectionString))
{
lock (_lockObject)
{
try
{
var collection = db.GetCollection<Product>("products");
var result = collection.DeleteMany(p => p.Stock == 0);
Console.WriteLine($"刪除了 {result} 個庫存為0的產品");
}
catch (Exception ex)
{
Console.WriteLine($"刪除操作失敗: {ex.Message}");
}
}
}
}
private void QueryProducts()
{
using (var db = new LiteDatabase(_connectionString))
{
try
{
var collection = db.GetCollection<Product>("products");
var query = collection.Query()
.Where(p => p.Price >= 100 && p.Stock > 0)
.OrderByDescending(p => p.CreateTime)
.Select(p => new { p.Name, p.Price, p.Stock })
.Limit(5)
.ToList();
Console.WriteLine("\n最新的5個高價產品:");
foreach (var item in query)
{
Console.WriteLine($"名稱: {item.Name}, 價格: {item.Price:C}, 庫存: {item.Stock}");
}
}
catch (Exception ex)
{
Console.WriteLine($"查詢操作失敗: {ex.Message}");
}
}
}
public void Dispose()
{
// 實現 IDisposable
GC.SuppressFinalize(this);
}
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("開始多線程數據庫訪問測試...\n");
using (var demo = new MultiThreadAccess())
{
// 執行多次并發測試
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"\n=== 測試輪次 {i + 1} ===\n");
demo.SafeConcurrentAccess();
Thread.Sleep(1000); // 暫停一秒后進行下一輪測試
}
}
Console.WriteLine("\n測試完成!按任意鍵退出...");
Console.ReadKey();
}
}
}
圖片
Connection = ConnectionType.Shared 這是重點。
性能注意事項
- LiteDB 是單線程數據庫,并發控制依賴于應用層鎖
- 對于高并發場景,考慮使用更強大的數據庫系統
- 優化鎖的使用范圍,減少鎖定時間
- 盡可能使用細粒度鎖
- 避免長時間持有鎖
- 使用 Parallel.For 和 Task 進行并發操作
- 實現詳細的錯誤處理和日志記錄
總結
這篇文章主要討論了LiteDB數據庫的并發控制機制。文章介紹了共享鎖和排他鎖兩種鎖機制的實現方式,以及在多線程環境下如何安全地進行數據讀寫操作。同時還探討了多設備數據同步的實現方案,包括時間戳比對和沖突解決策略。由于LiteDB是單線程數據庫,文章強調了在應用層實現適當的鎖策略和同步技術的重要性,以確保數據一致性和完整性。