SQLite: ведение журнала записи (режим журнала WAL) с подключенной базой данных

Работая над оптимизацией базы данных, мы разделили нашу базу данных на две базы данных: db и db2. Фоновый поток с низким приоритетом вставляется в db2. Некоторые запросы к db объединяются с db2, поэтому нам нужно присоединить db2 к db. Мы включаем WAL, потому что хотим, чтобы все было многопоточным.

SQLiteDatabase db = SQLiteDatabase.openDatabase(dbPath, ...);
db.enableWriteAheadLogging();
db.execSQL("attach " + db2path + " as db2");

Чтобы понять проблему, мы запускаем простой двухпоточный тест. Первый поток вставляет строки в БД, а второй поток выбирает из БД. Каждый поток печатает дельту времени из предыдущего цикла и время, которое мы были в базе данных.

thread 1 loop:                             | thread 2 loop:
    t1 = getTime()                         |   t1 = getTime()
    db2.execSQL("insert into ....");       |   db2.execSQL("select ....");
    t2 = t3                                |   t2 = t3
    t3 = getTime()                         |   t3 = getTime()
    log("i: "+(t3-t1)+", delta: "+(t2-t1)) |   log("s: "+(t3-t1)+", delta: "+(t2-t1))

То, что мы видим, это то, что выбранный поток блокирует поток вставки. Это можно подчеркнуть, сделав огромный (и медленный) выбор и крошечную вставку. Вы увидите, что время вставки и дельта увеличиваются примерно до времени выбора. Если мы не запускаем медленные потоки, поток вставки значительно ускоряется.

Копаясь в исходном коде SQLiteDatabase, я нашел следующие строки в SQLiteDatabase#enableWriteAheadLogging():

// make sure this database has NO attached databases because sqlite's write-ahead-logging
// doesn't work for databases with attached databases
if (mHasAttachedDbsLocked) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "this database: " + mConfigurationLocked.label
                + " has attached databases. can't  enable WAL.");
    }
    return false;
}

Теперь к моим вопросам:

  1. В чем смысл комментария? Что именно не работает? Это какой-то старый код, оставленный позади? В документации к базе данных ATTACH ( https://www.sqlite.org/lang_attach.html) явно указано, что с ATTACH + WAL все в порядке (с небольшой оговоркой.)

  2. Почему код привязки Android пытается защитить нас от внутренних проблем SQLite? На мой взгляд, это должен быть тонкий интерфейсный слой.

Изменить: я сообщил об этом как об ошибке в трекере проблем AOSP. Обновится, если там появится ответ.

1 ответ

WAL позволяет читателям и писателю одновременно, но только из разных связей. Вы никогда не должны использовать одно и то же соединение (SQLiteDatabase объект) из нескольких потоков.

Настройка WAL является постоянной; Вам не нужно выполнять его каждый раз после открытия базы данных.

  1. Смысл комментария именно то, что он говорит. (Никто не гарантирует, что этот комментарий правильный.)

  2. Иногда фреймворк Android пытается быть умным. Но вы можете просто выполнить PRAGMA journal_mode = WAL вручную.

Другие вопросы по тегам