我的Android应用程序中有一个数据库,但是每次尝试更新(写入)数据库时,都会创建两个附加文件shm
和wal
。特别是wal
文件越来越大,即使我将相同的数据插入数据库,当我尝试获取最后修改时间时,它也困扰着我,数据仅写入wal
文件中,并且不是实际的数据库。
我的问题是,如何在每次更新后强制其将数据写入数据库并删除临时文件?
答案 0 :(得分:3)
我的问题是如何强制其将数据写入数据库 并在每次更新后删除临时文件?
是的,您可以强制/启动检查点。
关闭数据库/连接应该对其进行检查,但是从资源角度考虑可能是昂贵的(因为您必须重新打开它)。
或者您可以使用:-
PRAGMA wal_checkpoint(TRUNCATE)
但是,您应该考虑使用
PRAGMA wal_checkpoint
在截断前后查看WAL是否忙/检查点完成了所有操作(您可能希望修改下面的示例以检查值wal_busy等(如果采用的话)(该示例来自数据库备份和数据库)已关闭))。
wal_checkpoint编译指示返回一行,其中包含三个整数列。第一列通常为0,但如果阻止RESTART或FULL或TRUNCATE检查点完成,则为1
我建议您阅读Write-Ahead Logging和PRAGMA 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
产生:-
答案 1 :(得分:1)
这两个文件的存在意味着您的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中所述。
答案 2 :(得分:0)
这是 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)
来保存一些代码。