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

SQLite主数据库文件未更新,-wal文件更新,导出无法获取新增数据

问题原因

你遇到的问题源于SQLite的WAL(Write-Ahead Log)写入模式:

  • Android默认给SQLite启用WAL模式,新增/修改的数据会先写入database-wal日志文件,而非直接更新主database.db文件。
  • 只有当数据库空闲、日志达到阈值等特定条件触发时,SQLite才会自动把WAL里的数据合并到主DB文件。直接拷贝主DB文件,自然会漏掉这些未合并的新增数据。
解决方案

要导出完整数据,推荐以下两种方案:

方案1:强制合并WAL数据到主DB后再拷贝

在拷贝主DB文件前,执行SQLite检查点命令,强制将WAL中的所有数据合并到主数据库,之后拷贝的database.db就会包含所有数据。

修改后的代码如下:

protected Boolean exportarBD(Context context, Activity activity) throws IOException {
    Boolean exported = false;

    if (!Permisos.validarWriteExternalStorage(context, activity)){
        return exported;
    }

    File backupDir = new File(context.getExternalFilesDir(null), "mydatabase/backup");
    if (!backupDir.exists()) {
        backupDir.mkdirs(); // 创建目录(如果不存在)
    }

    // 定义导出文件名
    String timestamp = new Date().toString();
    File exportFile = new File(backupDir, "DB_exported_" + timestamp + ".db");

    // 先执行WAL检查点,合并数据到主DB
    SQLiteDatabase db = null;
    try {
        db = SQLiteDatabase.openDatabase(
            context.getDatabasePath("database.db").getAbsolutePath(),
            null,
            SQLiteDatabase.OPEN_READWRITE
        );
        // 强制执行完整检查点,将WAL数据合并到主DB
        db.execSQL("PRAGMA wal_checkpoint(FULL);");
    } catch (SQLiteException e) {
        e.printStackTrace();
        return exported;
    } finally {
        if (db != null && db.isOpen()) {
            db.close();
        }
    }

    File dbFile = new File(context.getApplicationContext().getDatabasePath("database.db").getAbsolutePath());
    if (dbFile.exists()) {
        // 拷贝主DB文件
        FileInputStream fis = new FileInputStream(dbFile);
        FileOutputStream fos = new FileOutputStream(exportFile);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = fis.read(buffer)) > 0) {
            fos.write(buffer, 0, length);
        }

        fos.flush();
        fos.close();
        fis.close();

        exported = true;
    } else {
        exported = false;
    }

    return exported;
}

方案2:同时拷贝WAL和SHM文件

如果不想合并数据,可以同时导出database.db、database-wal和database-shm三个文件,后续恢复时将这三个文件放在同一目录下,SQLite会自动读取WAL中的数据。

需在代码中添加对这两个文件的拷贝逻辑:

// 拷贝WAL文件
File walFile = new File(context.getDatabasePath("database.db").getParent(), "database-wal");
if (walFile.exists()) {
    String timestamp = new Date().toString();
    File exportWal = new File(backupDir, "DB_exported_" + timestamp + ".wal");
    copyFile(walFile, exportWal);
}

// 拷贝SHM文件
File shmFile = new File(context.getDatabasePath("database.db").getParent(), "database-shm");
if (shmFile.exists()) {
    String timestamp = new Date().toString();
    File exportShm = new File(backupDir, "DB_exported_" + timestamp + ".shm");
    copyFile(shmFile, exportShm);
}

// 新增拷贝工具方法
private void copyFile(File source, File dest) throws IOException {
    FileInputStream fis = new FileInputStream(source);
    FileOutputStream fos = new FileOutputStream(dest);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = fis.read(buffer)) > 0) {
        fos.write(buffer, 0, length);
    }
    fos.flush();
    fos.close();
    fis.close();
}
注意事项
  • 执行检查点时,要确保没有其他线程对数据库进行写操作,避免数据不一致。
  • 方案1导出的单个DB文件可直接用于其他SQLite环境;方案2需要三个文件同时存在才能读取完整数据。

内容的提问来源于stack exchange,提问作者Luis M. Rios

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:23:24