Sqlite 断电数据丢失
在Android 9的机器上,遇到一个问题,sqlite 数据写入完成以后,立马断电。重启以后刚刚写入的数据丢失。
初步排查,数据库同步问题,数据写入以后没有立马同步到磁盘。与数据库打开模式相关。
打开模式在SQLiteConnection这个类:
上代码.
Android 9:
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
final boolean walEnabled =
(mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
// Use compatibility WAL unless an app explicitly set journal/synchronous mode
// or DISABLE_COMPATIBILITY_WAL flag is set
final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal();
if (walEnabled || useCompatibilityWal) {
setJournalMode("WAL");
if (mConfiguration.syncMode != null) {
setSyncMode(mConfiguration.syncMode);
} else if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
} else {
setSyncMode(SQLiteGlobal.getWALSyncMode());
}
} else {
setJournalMode(mConfiguration.journalMode == null
? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
setSyncMode(mConfiguration.syncMode == null
? SQLiteGlobal.getDefaultSyncMode() : mConfiguration.syncMode);
}
}
}
Android 12:
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
final boolean walEnabled =
(mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
// Use compatibility WAL unless an app explicitly set journal/synchronous mode
// or DISABLE_COMPATIBILITY_WAL flag is set
final boolean isCompatibilityWalEnabled =
mConfiguration.isLegacyCompatibilityWalEnabled();
if (walEnabled || isCompatibilityWalEnabled) {
setJournalMode("WAL");
if (mConfiguration.syncMode != null) {
setSyncMode(mConfiguration.syncMode);
} else if (isCompatibilityWalEnabled) {
setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
} else {
setSyncMode(SQLiteGlobal.getWALSyncMode());
}
maybeTruncateWalFile();
} else {
setJournalMode(mConfiguration.journalMode == null
? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
setSyncMode(mConfiguration.syncMode == null
? SQLiteGlobal.getDefaultSyncMode() : mConfiguration.syncMode);
}
}
}
默认打开mConfiguration的属性只有openFlags被添加一个SQLiteDatabase.CREATE_IF_NECESSARY,其他都是空。
所以关键属性影响在于:
android 9:mConfiguration.useCompatibilityWal()
android12:mConfiguration.isLegacyCompatibilityWalEnabled();
android 9:
boolean useCompatibilityWal() {
return journalMode == null && syncMode == null
&& (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0;
}
android12:
boolean isLegacyCompatibilityWalEnabled() {
return journalMode == null && syncMode == null
&& (openFlags & SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL) != 0;
}
在android 9上默认使用WAL模式,
Android 12上
<string name="db_default_journal_mode" translatable="false">TRUNCATE</string>
所以其他几个配置:
<string name="db_wal_sync_mode" translatable="false">NORMAL</string>
<string name="db_default_sync_mode" translatable="false">FULL</string>
所以Android9:WAL+NORMAL。Android12:TRUNCATE+FULL.
结论来了:
sync_mode 影响,导致断电过快,数据直接丢失。
具体部分:[https://sqlite.org/pragma.html#pragma_synchronous]看官方介绍。(https://sqlite.org/pragma.html#pragma_synchronous)
解决方案:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
SQLiteDatabase.OpenParams.Builder builder = new SQLiteDatabase.OpenParams.Builder();
builder.setSynchronousMode("FULL");
devOpenHelper.setOpenParams(builder.build());
}
强制使用FULL 模式。
over