使用SqlSugar讀取Sqlite數據庫,項目運行過程中間歇性拋出以下異常:
SqlSugar.SqlSugarException:“中文提示 : 連接數據庫過程中發生錯誤,檢查服務器是否正常連接字符串是否正確,錯誤信息:Connection was closed, statement was terminatedDbType="Sqlite";ConfigId="".
English Message : Connection open error . Connection was closed, statement was terminatedDbType="Sqlite";ConfigId="" ”
經定位,異常拋出位置在一個很普通的查詢操作上:
public List<SqliteDiskEntity> GetDisksByPDiskId(string env, string project, int pDiskId) { return _db.Queryable<SqliteDiskEntity>().Where(x => x.PDiskId == pDiskId).ToList(); }
連接字符串沒有問題,數據庫文件也正常存在,且異常並非每次必現,而是偶發性的。
排查過程
1. 排除連接字符串問題
連接配置如下,看起來沒有明顯問題:
1 private static SqlSugarClient CreateClient(string dbPath) 2 { 3 return new SqlSugarClient(new ConnectionConfig 4 { 5 ConnectionString = $"Data Source={dbPath};Version=3;Journal Mode=Wal;BusyTimeout=5000;", 6 DbType = SqlSugar.DbType.Sqlite, 7 IsAutoCloseConnection = true, 8 }); 9 }
已開啓 WAL 模式、設置了 BusyTimeout、配置了 IsAutoCloseConnection = true,表面上不應該出問題。
2. 調試器線程分析 —— 發現併發訪問
在 Visual Studio 中暫停調試,查看線程窗口時發現了關鍵線索:
線程 ID 當前位置
34996 DiskSubscribeTimer_Triggered(object, SubscribeTaskArgs)
30716 ExecuteSubscribeTask(SubscribeTaskArgs) → RunSubscribeTaskAsync()
38276 DiskSubscribeTimer_Triggered(object, SubscribeTaskArgs)
雖然代碼中用 ConcurrentDictionary 防止了同一個磁盤的併發執行,但不同的訂閲任務仍然會並行運行,共享同一個 _db 實例。
問題根因及解決
_db是SqlSugarClient實例,所以,
此實例不是線程安全的。 當多個線程同時通過同一個 SqlSugarClient 實例操作數據庫時,內部的連接/命令對象會發生競爭,導致 SQLite 底層返回 SQLITE_MISUSE(即 bad parameter or other API misuse)。
整個調用鏈路如下:
這個錯誤的迷惑性在於:
- 連接字符串完全正確
- IsAutoCloseConnection = true 看似已經處理了連接釋放
- 異常只在多任務併發時偶發出現
- 異常信息指向"連接錯誤",容易誤導排查方向
將SqlSugarClient(非線程安全)改為SqlSugarScope(線程安全),即可
SqlSugarClient vs SqlSugarScope的區別,列個對比