You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin安卓App中SQLite频繁触发database locked/busy异常排查

解决SQLite "database is locked"/"database is busy" 第二次执行崩溃问题

看起来你遇到了SQLite连接管理导致的经典锁问题——这类错误90%以上都是因为连接重复创建、未正确释放,或者线程冲突导致的。结合你的代码,我给你梳理几个关键修复点:


1. 修复连接生命周期管理:别手动Close() + using重复操作

你的代码里多处出现using (db)后又手动调用db.Close(),这是冗余且危险的:using语句会自动调用SQLiteConnection.Dispose(),而Dispose()内部已经会关闭连接。手动提前关闭会导致连接状态异常,第二次执行时可能残留未释放的锁。

错误示例:

var db = _dataBaseConnection.GetSqLiteConnection();
using (db) {
    // ...操作
    db.Close(); // 多余!会导致连接状态异常
}

修复后:

// 直接在using里创建连接,自动管理生命周期
using (var db = _dataBaseConnection.GetSqLiteConnection()) {
    // ...数据库操作
    // 不需要手动Close(),using会自动Dispose并关闭连接
}

2. 统一连接实例:避免重复创建多个连接

SQLite对多连接的支持很差(除非启用WAL模式),如果你的GetSqLiteConnection()每次都返回新的连接实例,第二次执行时可能前一个连接还没完全释放,导致数据库锁冲突。

建议改成:

  • 全局持有一个单例连接实例(在应用启动时创建,整个应用生命周期复用)
  • 或者用依赖注入框架注册SQLiteConnection为单例服务

3. 内部方法复用同一个连接,不要额外处理

你的UpdateInventory方法接收连接参数是对的,但要确保这个连接是外层using管理的实例,不要在内部做任何关闭/释放操作——让外层的using统一处理。

优化后的UpdateInventory:

private void UpdateInventory(DbInventory inventory, SQLiteConnection db) {
    _logger.Debug("Enter");
    try {
        // 直接用传入的连接,外层using会负责释放
        var result = db.Update(inventory);
    } catch (SQLiteException ex) {
        _logger.Error(ex);
        throw;
    }
}

4. InsertOrUpdateInventoryEntry的连接处理优化

这个方法的disposeFlg逻辑是对的,但要注意:如果是传入的连接,绝对不要关闭/释放它,因为调用方可能还要复用。同时去掉手动的db.Close():

修复后片段:

if (disposeFlg) {
    // 只需要Dispose(),内部会自动关闭
    db.Dispose();
}

5. 启用WAL模式(推荐)

SQLite默认的DELETE journal模式并发支持极差,启用WAL(Write-Ahead Logging)模式可以大幅提升并发性能,减少锁冲突。在应用启动时执行一次即可(设置是持久化的):

using (var db = _dataBaseConnection.GetSqLiteConnection()) {
    db.Execute("PRAGMA journal_mode=WAL;");
}

6. 确保数据库操作在同一线程(或线程安全调度)

SQLite连接不是线程安全的,不要在多个线程同时使用同一个连接,也不要在不同线程创建多个连接。如果你的应用有后台线程操作数据库,建议用单线程调度器(比如TaskScheduler.FromCurrentSynchronizationContext()或者自定义线程池)来统一处理所有数据库操作。


完整修复后的核心方法示例

比如InsertOrUpdateInventoryWithoutEntries修改后:

public void InsertOrUpdateInventoryWithoutEntries(Inventory inventory) {
    _logger.Debug("Enter");
    try {
        using (var db = _dataBaseConnection.GetSqLiteConnection()) {
            var existingDbInventory = db.Find<DbInventory>(dbInventory => dbInventory.Code == inventory.Code);
            if (existingDbInventory != null) {
                if (!existingDbInventory.Finished) 
                {
                    existingDbInventory.Description = inventory.Description;
                    existingDbInventory.OpenTime = inventory.OpenTime;
                    existingDbInventory.Finished = inventory.Finished;
                    existingDbInventory.UseInventoryList = inventory.UseInventoryList;
                    existingDbInventory.PostedToServer = inventory.PostedToServer;
                    existingDbInventory.InventoryListIsDownloaded = inventory.InventoryListIsDownloaded;
                    UpdateInventory(existingDbInventory, db);
                }
            } else {
                db.Insert(DbInventory.FromInventory(inventory));
            }
        }
    } catch (SQLiteException ex) {
        _logger.Error(ex);
        throw;
    }
}

按照这些步骤修复后,第二次执行的锁问题应该就能解决了。

内容的提问来源于stack exchange,提问作者Niklas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:45:13