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

解决SQLITE database.locked错误:单线程下事务串行执行方案

解决SQLite事务冲突导致的database.locked问题

你的问题核心在于信号打断了正在执行的数据库事务:当你的单线程正在dbExecSql的事务(从BEGIN到END)过程中,其他线程发来的信号会直接中断当前执行流程,转而调用dbDelete。而SQLite的单个连接是单线程独占的,不允许在一个未完成的事务上执行其他操作(尤其是DETACH这种对连接状态有影响的命令),这就直接触发了database.locked错误。

下面是具体的解决思路和代码修改方案:

核心思路:让维护操作(dbDelete)排队等待事务完成

不要在信号处理函数里直接执行dbDelete,而是用异步安全的标志位标记需要执行维护操作,然后在dbExecSql的事务安全节点(每次事务结束后)检查并执行dbDelete,确保两个操作不会重叠。

步骤1:添加维护状态标记

在你的Database类中添加两个原子变量(保证线程安全和异步信号安全):

#include <atomic>

class Database {
private:
    // 标记是否有等待执行的维护任务
    std::atomic<bool> m_pending_maintenance{false};
    // 标记当前是否正在执行维护操作,防止重复触发
    std::atomic<bool> m_in_maintenance{false};
    // 其他成员变量...
};

步骤2:修改信号触发逻辑

其他线程不要直接调用dbDelete,而是设置m_pending_maintenance为true。如果是用POSIX信号,信号处理函数里只能做异步安全的操作,所以只设置原子变量:

// 信号处理函数(异步安全)
void handle_maintenance_signal(int signum) {
    // 假设db是全局或者可以安全访问的Database实例指针
    db->m_pending_maintenance = true;
}

步骤3:修改dbExecSql,在安全点检查维护任务

在dbExecSql的事务开始前和结束后,检查是否有等待执行的维护任务,确保只有当前事务完成后才执行dbDelete:

int Database::dbExecSql() {
    // 事务开始前先检查维护任务(避免刚启动就被打断)
    while (m_pending_maintenance && !m_in_maintenance) {
        m_in_maintenance = true;
        dbDelete();
        m_in_maintenance = false;
        m_pending_maintenance = false;
    }

    // 执行正常的写入事务
    sqlite3_stmt* stmt = nullptr;
    int rc = SQLITE_OK;

    rc = sqlite3_prepare_v2(db, insertTableSQL.c_str(), -1, &stmt, 0);
    if (rc != SQLITE_OK) {
        // 错误处理...
        return rc;
    }

    rc = sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_finalize(stmt);
        // 错误处理...
        return rc;
    }

    // 绑定、执行插入逻辑...
    rc = sqlite3_bind_int(stmt, j + 1, insertValueInt);
    // ...省略其他绑定和执行代码
    rc = sqlite3_step(stmt);
    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    // 提交事务
    rc = sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_finalize(stmt);
        // 错误处理...
        return rc;
    }

    rc = sqlite3_finalize(stmt);
    if (rc != SQLITE_OK) {
        // 错误处理...
        return rc;
    }

    // 事务结束后再次检查维护任务
    if (m_pending_maintenance && !m_in_maintenance) {
        m_in_maintenance = true;
        dbDelete();
        m_in_maintenance = false;
        m_pending_maintenance = false;
    }

    return rc;
}

步骤4:优化dbDelete的原子性

把dbDelete的所有操作包裹在一个事务里(虽然ATTACH/DETACH在事务中的行为特殊,但可以确保整个维护操作是连续执行的,不会被其他逻辑打断):

int Database::dbDelete() {
    int rc = SQLITE_OK;
    char* err_msg = nullptr;

    // 开始维护事务
    rc = sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        cout << "ERROR: Maintenance Transaction Start: " << err_msg << endl;
        sqlite3_free(err_msg);
        return rc;
    }

    // 原有ATTACH、COPY、DETACH、DELETE逻辑...
    attachQuery = "ATTACH DATABASE '" + db_log_dir + "/" + system_id + "_" + dbLogName + ".sql'" + " AS '" + system_id + "_" + dbLogName + "';";
    detachQuery = "DETACH DATABASE '" + system_id + "_" + dbLogName + "';";
    copyTableQuery = "CREATE TABLE '" + system_id + "_" + dbLogName + "'.'" + tableName + "_" + dbLogName + "' AS SELECT * FROM main." + tableName;
    deleteQuery = dbDeleteSelected(tableName, timeStart, customLog);

    rc = sqlite3_exec(db, attachQuery.c_str(), NULL, 0, &err_msg);
    cout << "ATTACH: " << rc << endl;
    if (rc != SQLITE_OK) {
        cout << "ERROR: Hour Log Attach: " << err_msg << endl;
        cout << "SQL -> " << attachQuery.c_str() << endl;
        sqlite3_free(err_msg);
        sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
        return rc;
    }

    // ...省略COPY、DETACH、DELETE的原有逻辑

    // 提交维护事务
    rc = sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        cout << "ERROR: Maintenance Transaction End: " << err_msg << endl;
        sqlite3_free(err_msg);
        return rc;
    }

    return rc;
}

为什么这样能解决问题?

  • 我们把dbDelete的执行时机从信号中断点,转移到了dbExecSql事务的安全间隙(事务完成后),确保同一时刻只有一个数据库操作在执行。
  • 原子变量保证了线程间和信号处理中的状态修改是安全的,不会出现竞态条件。
  • 给dbDelete加上事务,确保整个维护操作是原子性的,不会被中途打断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:22:48