【问题标题】:Remove temporary room sqlite files?删除临时房间 sqlite 文件?
【发布时间】:2021-02-04 15:03:55
【问题描述】:

我的 android 应用程序中有一个数据库,但每次我尝试更新它(写入它)时,都会创建两个额外的文件 shmwal。即使我将相同的数据插入数据库,wal 文件也越来越大,当我尝试获取上次修改时间时,它也让我感到困扰,数据仅写入wal 文件而不是实际数据库.

我的问题是如何强制它在每次更新后将数据写入数据库并删除临时文件?

【问题讨论】:

    标签: android sqlite android-sqlite android-room


    【解决方案1】:

    我的问题是如何强制它将数据写入数据库 并在每次更新后删除临时文件?

    是的,您可以强制/启动检查点。

    关闭数据库/连接应该检查点它,但在资源方面可能代价高昂(因为您必须重新打开它)。

    您也可以使用:-

    PRAGMA wal_checkpoint(TRUNCATE)
    

    但是,你应该考虑使用

    PRAGMA wal_checkpoint 
    

    在截断之前和之后查看 WAL 是否繁忙/检查点完成所有操作(如果您采用它,您可能希望修改下面的示例以检查 wal_busy 等值(示例来自 db 备份和数据库反正已经关闭了))。

    wal_checkpoint pragma 返回包含三个整数列的单行。第一列通常为 0,但如果 R​​ESTART、FULL 或 TRUNCATE 检查点被阻止完成,则为 1

    我建议阅读Write-Ahead LoggingPRAGMA Statements - PRAGMA schema.wal_checkpoint;

    当我尝试获取上次修改时间时,数据仅写入 wal 文件,而不是实际的数据库。

    -wal 文件被视为数据库并被读取/更新,如果它们在 -wal 文件中,则从 -wal 文件中提取任何更改。 SQLite 相当安全/坚固。

    示例

    以下是检查点的示例:-

    public static int checkpointDB(SupportSQLiteDatabase db) {
    
        String TAG = "CHECKPOINTDB";
        int wal_busy = 99, wal_log = 99, wal_checkpointed =99;
    
        Cursor csr = db.query("PRAGMA journal_mode", null);
        if (csr.moveToFirst()) {
            String mode = csr.getString(0);
            Log.d(TAG, "Mode is " + mode);
            if (mode.toLowerCase().equals("wal")) {
                csr = db.query("PRAGMA wal_checkpoint", null);
                if (csr.moveToFirst()) {
                    wal_busy = csr.getInt(0);
                    wal_log = csr.getInt(1);
                    wal_checkpointed = csr.getInt(2);
                }
                Log.d(TAG,"Checkpoint pre checkpointing Busy = " + String.valueOf(wal_busy) + " LOG = " + String.valueOf(wal_log) + " CHECKPOINTED = " + String.valueOf(wal_checkpointed) );
                csr = db.query("PRAGMA wal_checkpoint(TRUNCATE)", null);
                csr.getCount();
                csr = db.query("PRAGMA wal_checkpoint", null);
                if (csr.moveToFirst()) {
                    wal_busy = csr.getInt(0);
                    wal_log = csr.getInt(1);
                    wal_checkpointed = csr.getInt(2);
                }
                Log.d(TAG,"Checkpoint post checkpointing Busy = " + String.valueOf(wal_busy) + " LOG = " + String.valueOf(wal_log) + " CHECKPOINTED = " + String.valueOf(wal_checkpointed) );
            }
        }
        csr.close();
        return wal_busy;
    }
    

    可以调用,(其中mPDB是构建的PersonDatabase对象,上面在PersonDatabase(@Database)类中) ,使用类似的东西:-

    PersonDatabase.checkpointDB(mPDB.getOpenHelper().getWritableDatabase());
    

    一个示例用法导致:-

    2019-07-13 08:15:54.997 18556-18556/s.e.s56720641roomwithmanytomany D/CHECKPOINTDB: Mode is wal
    2019-07-13 08:15:54.997 18556-18556/s.e.s56720641roomwithmanytomany D/CHECKPOINTDB: Checkpoint pre checkpointing Busy = 0 LOG = 0 CHECKPOINTED = 0
    2019-07-13 08:15:55.000 18556-18556/s.e.s56720641roomwithmanytomany D/CHECKPOINTDB: Checkpoint post checkpointing Busy = 0 LOG = 0 CHECKPOINTED = 0
    

    导致:-

    【讨论】:

    • 有没有其他方法可以完全通过房间本身来完成?
    • @MaskedMan 仅通过关闭数据库,这不是典型的 oo 操作,而是典型的标准 sqlite 安装之外的更多系统管理。
    • 不要忘记在每次重新分配之前致电csr.close()
    【解决方案2】:

    这两个文件的存在意味着您的 SQLite DB 正在WAL mode 中运行,该机制已被广泛记录。 这是一种持久模式,可以通过运行此语句一次来实现:

    PRAGMA journal_mode=WAL;

    请参阅第 3.3 节。来自上面链接的 WAL 模式的持久性。 另一个用于更深入解释的有用资源:Temporary Files Used By SQLite

    如果这种行为困扰您,您可以简单地发出以下语句:

    PRAGMA journal_mode=DELETE;

    setJournalMode(JournalMode.TRUNCATE)Android Room docs 中所述。

    【讨论】:

    • 感谢您的回答和指导。快乐的编码:) 但是有没有办法在不设置日志模式的情况下将wal 刷新到数据库?
    【解决方案3】:

    这是 MikeT's answer 的 Kotlin 版本:

    fun SupportSQLiteDatabase.performCheckpoint(): Boolean {
        val mode = query("PRAGMA journal_mode").use { cursor ->
            if (!cursor.moveToNext()) {
                return false // not sure if this can happen
            }
            cursor.getString(0)
        }
        println("mode: $mode")
        if (mode.toLowerCase(Locale.ROOT) != "wal") {
            return false
        }
        executePragmaThenLog("PRAGMA wal_checkpoint")
        executePragmaThenLog("PRAGMA wal_checkpoint(TRUNCATE)")
        executePragmaThenLog("PRAGMA wal_checkpoint")
        return true
    }
    
    private fun SupportSQLiteDatabase.executePragmaThenLog(sql: String) {
        query(sql).use { cursor ->
            if (cursor.moveToFirst()) {
                val busy = cursor.getInt(0)
                val log = cursor.getInt(1)
                val cp = cursor.getInt(2)
                val label = sql.removePrefix("PRAGMA ")
                println("$label: busy=$busy, log=$log, checkpointed=$cp")
            }
        }
    }
    

    注意:您也可以使用DatabaseUtils.dumpCursorToString(cursor) 来保存一些代码。

    【讨论】:

    • 最后的 wal_checkpoint 调用有什么意义吗?它似乎总是给出与前面的 wal_checkpoint(TRUNCATE) 相同的值
    猜你喜欢
    • 1970-01-01
    • 2019-12-30
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多