C#使用Microsoft.Data.Sqlite时如何防止数据库连接被完全自动关闭
该现象不是SQLite引擎主动断开连接,是Microsoft.Data.Sqlite连接池的默认回收机制导致:主流版本的Microsoft.Data.Sqlite默认会回收闲置超过300秒(5分钟)的加密连接,后续新请求需要重新建立物理连接。由于加密库(SQLCipher)建立连接时需要执行PBKDF2密钥派生、校验加密头、初始化页缓存,这部分开销和应用冷启动首次查询的开销完全一致,就是感知到的卡顿来源。
之前调整Cache、Pooling参数无效,是因为没有修改连接池闲置回收、最小常驻连接数的核心配置。
方案1:调整连接池参数,延长闲置连接保留时长
直接在连接字符串中指定连接池闲置回收规则,不需要修改现有using块写法,侵入性最低,是最推荐的方案。
修改后的连接字符串构造代码:
var connectionString = new SqliteConnectionStringBuilder($"Data Source=source.sqlite; Password='{password}';") { Mode = SqliteOpenMode.ReadOnly, Pooling = true, // 连接池闲置超时设为24小时,单位为秒 ConnectionIdleTimeout = 86400, // 连接池常驻至少1个已初始化的可用连接,不会被闲置回收 MinimumPoolSize = 1 }.ToString();
说明:MinimumPoolSize=1会保证连接池始终持有一个完成密钥派生、缓存初始化的可用连接,从根源避免冷启动开销。
方案2:后台轻量保活(适配旧版Nuget包场景)
如果使用的Microsoft.Data.Sqlite版本过旧,没有ConnectionIdleTimeout、MinimumPoolSize配置项,可以在应用生命周期内启动一个低优先级定时器,每隔3分钟执行一次极轻量的查询,保持连接活跃不被回收。
保活逻辑示例:
// 应用启动时初始化保活定时器,执行周期3分钟 var keepAliveTimer = new PeriodicTimer(TimeSpan.FromMinutes(3)); _ = Task.Run(async () => { while (await keepAliveTimer.WaitForNextTickAsync()) { try { using var conn = new SqliteConnection(connectionString); await conn.OpenAsync(); using var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT 1;"; await cmd.ExecuteScalarAsync(); } catch { // 保活执行失败无需特殊处理,不影响正常业务逻辑 } } });
注意:需要将定时器对象设为全局可达,避免被GC回收;保活查询的资源开销极低,不会影响应用性能。
方案3:单例长连接(适配只读低并发场景)
WPF桌面端属于低并发访问场景,且当前使用ReadOnly模式打开数据库,不存在写入锁冲突问题,可以直接关闭连接池,维护一个全局长连接复用,完全避免连接重建开销。
注意:该方案不适合高并发写入场景,会引发锁竞争问题;全局连接不要放在
using块中,在应用退出时再手动释放即可。
示例写法:
// 应用启动时初始化全局只读连接 public static readonly SqliteConnection GlobalDbConnection = new SqliteConnection( new SqliteConnectionStringBuilder($"Data Source=source.sqlite; Password='{password}';") { Mode = SqliteOpenMode.ReadOnly, Pooling = false }.ToString()); // 应用启动完成后手动Open一次全局连接,后续所有查询直接复用该实例即可
- 不要为了避免断连去掉
using块:Microsoft.Data.Sqlite开启连接池时,Dispose操作不会真的关闭物理连接,只是将连接放回连接池复用,去掉using反而会引发连接泄漏。 - 加密数据库的首次连接开销来自SQLCipher的PBKDF2密钥派生逻辑,无法完全消除,只要避免闲置后重建物理连接,就能解决闲置后首次查询卡顿的问题。
- 调整
Cache为Shared模式无法解决连接回收问题,该参数仅控制同进程多连接的缓存共享逻辑,不影响连接池的回收规则。
内容的提问来源于stack exchange,提问作者difilo9635

