SQLite SQLiteLinuxWindowsPHPPython

SQLite: При параллельной работе приложение видит устаревшие данные

  • Обновлено
  • Чтение ≈ 3 минуты

Краткий ответ

SQLite может показывать «старые» данные при параллельной работе из-за продолжающейся транзакции чтения: в WAL читатель видит согласованный снимок до начала новой транзакции. Нужно завершить чтение и начать новый запрос, а не отключать согласованность.

В этой статье
  1. Что происходит и когда инструкция применима
  2. Как отличить возможные причины
  3. Пошаговая диагностика и исправление
  4. Как проверить исправление
  5. Если исправление не помогло

#Что происходит и когда инструкция применима

SQLite может показывать «старые» данные при параллельной работе из-за продолжающейся транзакции чтения: в WAL читатель видит согласованный снимок до начала новой транзакции. Нужно завершить чтение и начать новый запрос, а не отключать согласованность.

Характерный признак: Один процесс сделал COMMIT, другой продолжает видеть прежнее значение до завершения текущей транзакции либо после использования кэша приложения.

#Как отличить возможные причины

Возможная причинаКак узнатьЧто делать
Долгая транзакция чтенияВ WAL SELECT видит один снимок до конца транзакцииНовый клиент видит обновление, старый — нетЗавершать транзакцию между запросами
Кэш приложенияПамять процесса или ORM хранит старую сущностьПосле перезапуска кэш сбрасываетсяНастроить срок жизни/инвалидацию
Разные файлы SQLiteПриложения открыли разные .dbPRAGMA database_list показывает разные путиСогласовать строку подключения

#Пошаговая диагностика и исправление

  1. 1. Убедитесь, что запись была зафиксированаВ коде пишущего приложения проверьте успешный COMMIT после INSERT/UPDATE. Если транзакция откатана или процесс аварийно завершился, читатель закономерно не видит изменения.
  1. 2. Сравните пути двух подключенийНа каждом соединении выведите список присоединённых баз. Одинаковые логические имена ещё не доказывают один и тот же физический файл: сравнивайте абсолютные пути и окружение контейнера.
  2. Только чтение
    Linux · Терминал (Bash)Команда
    Только чтение. В реальном приложении запустите PRAGMA на каждом соединении, а не только из отдельного sqlite3.
    sqlite3 -readonly /srv/app/data/app.db 'PRAGMA database_list;'
  1. 3. Воспроизведите на отдельном тестовом файлеСначала в терминале Linux создайте новую учебную базу /tmp/sqlite-snapshot-demo.db (проверьте, что такого файла с данными нет). Затем откройте sqlite3 в двух отдельных терминалах на одном компьютере. В терминале A введите BEGIN; SELECT v FROM demo WHERE id=1; и ОСТАВЬТЕ транзакцию открытой. В B сделайте UPDATE из примера. После этого в A новый SELECT внутри той же транзакции вернёт old; введите COMMIT; и повторите SELECT — теперь ожидается new.
  2. Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
    Выберите подходящий вариант — это не команды для последовательного выполнения
    Вариант 1Однократное создание новой УЧЕБНОЙ базы в /tmp; убедитесь, что файл не существует.
    Linux · Терминал (Bash)С осторожностью
    Однократное создание новой УЧЕБНОЙ базы в /tmp; убедитесь, что файл не существует.
    sqlite3 /tmp/sqlite-snapshot-demo.db "PRAGMA journal_mode=WAL; CREATE TABLE demo(id INTEGER PRIMARY KEY, v TEXT); INSERT INTO demo(id,v) VALUES(1,'old');"

    Команда может изменить состояние системы или её настройки. Проверьте параметры и последствия перед запуском.

    Вариант 2Терминал A: после входа наберите BEGIN; SELECT v FROM demo WHERE id=1; и не завершайте транзакцию.
    Linux · Терминал (Bash)Команда
    Терминал A: после входа наберите BEGIN; SELECT v FROM demo WHERE id=1; и не завершайте транзакцию.
    sqlite3 /tmp/sqlite-snapshot-demo.db
    Вариант 3Терминал B: изменяет только учебную базу; выполняйте ПОСЛЕ первого SELECT в терминале A.
    Linux · Терминал (Bash)С осторожностью
    Терминал B: изменяет только учебную базу; выполняйте ПОСЛЕ первого SELECT в терминале A. Ожидается успешное завершение.
    sqlite3 /tmp/sqlite-snapshot-demo.db "UPDATE demo SET v='new' WHERE id=1;"

    Команда может изменить состояние системы или её настройки. Проверьте параметры и последствия перед запуском.

    Вариант 4Внутри открытого sqlite3 терминала A.
    SQLКоманда
    Внутри открытого sqlite3 терминала A. Первый SELECT — old, после COMMIT второй — new.
    SELECT v FROM demo WHERE id=1; COMMIT; SELECT v FROM demo WHERE id=1;
  1. 4. Проверьте, где в коде открывается транзакцияИщите ручной BEGIN, autocommit=False, ORM-сессии и курсоры, которые переживают один веб-запрос. В Python sqlite3 учитывайте особенности версий и режима autocommit; фиксируйте явный COMMIT/ROLLBACK при завершении логической операции.
  1. 5. Завершите старую транзакцию чтенияВ тестовом приложении завершите SELECT, закройте курсор и выполните COMMIT или ROLLBACK в зависимости от логики. Затем начните новый SELECT; в обычной схеме он должен увидеть новые зафиксированные строки.
  1. 6. Проверьте работу долгих фоновых заданийЕсли процесс читает миллион строк и держит транзакцию минуты, разбивайте выгрузку по логическим порциям, при этом учитывайте изменение данных между порциями. Для отчёта, которому нужен целостный снимок, старое отображение в течение транзакции является ожидаемым.
  1. 7. Исключите кэширование в приложенииНа тесте отключите только прикладной кэш конкретного запроса или выполните явное обновление ORM-сущности по инструкции используемой библиотеки. Если новая транзакция SQL показывает свежие данные, а интерфейс нет — проблема на уровне приложения.
  1. 8. Проверьте поведение после нового запросаВ тестовой среде сделайте две последовательные записи из отдельного клиента и убедитесь, что после завершения транзакции чтения новое подключение видит обе. Если результат не меняется, сравните время COMMIT и журналы приложения.

#Как проверить исправление

После завершения читающей транзакции и нового SELECT видна подтверждённая запись, при этом длительный отчёт продолжает последовательно видеть свой снимок.

#Если исправление не помогло

Если значение не обновилось, проверьте незафиксированную транзакцию писателя, другую базу, ORM-кэш, реплику на уровне приложения и ошибки сериализации.

Так эту ошибку тоже называют

Источники и обновления

Последнее обновление статьи: 2 ноября 2026. Версии программ и интерфейсы меняются — если шаги не совпадают с вашей версией, сверяйтесь с документацией производителя.