QDBM -> SQLite3
К конечном счёте, место QDBM занял SQLite3. Всего-то два дня боданий. Нагрузка, правда, выше, чем с использованием QDBM, но ниже, чем на файловой системе. Два дня боданий связаны с бесконечными deadlock, которые не удавалось воспроизвести на локальном сервере, но в который в первую же минуту впирался production, так что приходилось теоретизировать и осторожно тестировать по живому. Гугл с Яндексом молчали, ощущение такое, что с проблемой никто не сталкивался.
Через какое-то время у меня возникло ощущение, что дело как-то связано с транзакциями, подкрепляемое поведением журнала транзакций SQLite, в итоге я детально просмотрел всё, что нашёл вопросу транзакций этой СУБД и где-то наткнулся на фразу, что, дескать, SQLite3 совсем-совсем не предназначен для большого количество обновлений и ведёт себя в этом случае странно, так как транзакции у него по-умолчанию DEFERRED. Я не понял как связаны отложенные транзакции со странным поведением, но помнил из документации, что с версии 3.0.8 в SQLite поддерживает три типа транзакций — DEFERRED (отложенные), IMMEDIATE (можно читать) и EXCLUSIVE (полный lock). Проблема решилась стартом для запросов «INSERT OR REPLACE» транзакции EXCLUSIVE, для «SELECT» — IMMEDIATE (должен сказать, что используются только эти два вида запросов по ключу, база используется как хранилище).
Кстати, в SQLite2 все транзакции EXCLUSIVE, наверное такой проблемы там нет. Так что, если кто-то получит deadlock при переходи с «двойки» на «тройку» или при использовании SQLite3 в нагруженных проектах — имейте ввиду, in TRANSACTION veritas, в данном случае.
Кстати, есть тут специалисты, почему DEFERRED-транзакции вызывают такое поведение?
Добавлено позже: в SQLite 3.4.0 исправили проблему Busy Error, по описанию похоже на мою проблему, причём способ решения (если не накладывать новую версию) — EXCLUSIVE TRANSACTION.
Добавлено ещё позже: так и есть. На локальном сервере стояло 3.4.0, а удалённом — 3.3.8, причём 3.4.0 ставили из исходников, но её перекрыл пакет из rpm, а мы этого не заметили, поэтому локально deadlock не было, а на сервере он был.