![]() |
|
|||
WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
Свойство "isolation" базы данных определяет, когда изменения, внесенные в
базу данных одной операцией, становятся видимы
другим параллельным операциям. Если та же самая база данных читается и пишется, используя два различных
соединения с базой данных (два различных
объекта sqlite3, возвращенные отдельными
sqlite3_open()), и у этих двух соединений с
базой данных нет общего кэша,
читатель в состоянии видеть только полные переданные транзакции
от писателя. Частичные изменения писателем, которые не были переданы,
невидимы для читателя. Это верно независимо от того, работают ли эти два
соединения с базой данных в том же самом потоке, в различных потоках
того же самого процесса или в различных процессах. Это обычное и ожидаемое
поведение для систем базы данных SQL. Предыдущий параграф также верен (отдельные соединения с базой данных
изолированы друг от друга) в режиме общего кэша
пока read_uncommitted
pragma = off.
read_uncommitted pragma = off по умолчанию и поэтому если применение не
сделает ничего, чтобы включить его, это останется выключеннным.
Следовательно, если
read_uncommitted pragma не используется, чтобы изменить поведение по
умолчанию, изменения, внесенные одним соединением с базой данных, невидимы
для читателей в другом соединении с базой данных, разделяющем тот же самый
кэш, пока писатель не передает транзакцию. Если два соединения с базой данных разделят тот же самый кэш
и читатель позволил
read_uncommitted pragma, то читатель будет в состоянии видеть изменения,
внесенные писателем, прежде, чем транзакция писателя передастся.
Объединенное использование режима общего кэша
и read_uncommitted pragma
является единственным способом, которым одно соединение с базой данных видит
нейтральные изменения в другом соединении с базой данных.
При всех других обстоятельствах отдельные соединения с базой данных
полностью изолированы друг от друга. Кроме случая общего кэша
соединений с базой данных
PRAGMA read_uncommitted =
on, все транзакции в SQLite показывают изоляцию "serializable".
SQLite осуществляет сериализуемые транзакции, на самом деле преобразовывая
в последовательную форму запись. Может быть только единственный писатель
за один раз у базы данных SQLite. Могут быть многократные соединения с базой
данных, открытые в то же время, и все те соединения с базой данных могут
писать файл базы данных, но они должны меняться.
SQLite использует блокировки, чтобы преобразовать в последовательную форму
запись автоматически, это не что-то, о чем должны волноваться
приложения, используя SQLite. SQLite осуществляет изоляцию и управление совместным выполнением (и
атомичность) использованием переходных файлов журнала, которые появляются в
том же самом каталоге, как файл базы данных. Есть два главных режима
журналирования. Более старый "rollback mode" соответствует использованию
опций "DELETE", "PERSIST" или "TRUNCATE" в
journal_mode pragma.
В rollback mode изменения написаны непосредственно в файл базы данных, в то
время как одновременно отдельный файл журнала обратной перемотки построен,
который в состоянии вернуть базу данных к ее исходному состоянию,
если транзакция откатывается назад.
Режим Rollback (определяется как DELETE mode,
означая, что журнал обратной перемотки удален с диска в конце каждой
транзакции) является текущим поведением по умолчанию. С version 3.7.0 (2010-07-21) SQLite
также поддерживает "режим WAL". В режиме WAL изменения
не написаны оригинальному файлу базы данных. Вместо этого изменения пишутся
в отдельный "журнал с упреждающей записью" или файл
"WAL". Позже, после того, как транзакция передается,
те изменения будут перемещены от файла WAL назад в оригинальную базу данных в
операции, названной "контрольной точкой". Режим WAL позволен,
выполняя "
PRAGMA journal_mode=WAL". В режиме rollback SQLite осуществляет изоляцию, захватывая файл базы
данных и предотвращая любое чтение другими соединениями с базой данных, в то
время как каждая транзакция записи идет.
Читатели могут быть активными в начале записи, прежде чем любое содержание
сброшено на диск и в то время как все изменения все еще проводятся в частном
пространстве памяти писателя. Но прежде чем любые изменения внесены в файл
базы данных на диске, все читатели должны быть (временно) блокированы, чтобы
предоставить эксклюзивный доступ писателя к файлу базы данных. Следовательно,
читателям мешают видеть незавершенные транзакции на основании того,
база данных блокируется на то время, пока транзакция пишется на диск.
Только после того, как она полностью запишется и синхронизируется, читатели
возвращены назад в базу данных. Следовательно, читатели никогда не получают
шанс видеть частично написанные изменения. Режим WAL разрешает одновременно читать и писать.
Это может сделать так, потому что изменения не переписывают оригинальный
файл базы данных, а скорее входят в отдельный файл журнала с упреждающей
записью. Это означает, что читатели могут продолжить читать старое,
оригинальное, неизменное содержание от оригинального файла базы данных в то
же самое время, когда писатель добавляет данные
к журналу с упреждающей записью. В режиме WAL SQLite
показывает "snapshot isolation". Когда транзакция чтения
начинается, тот читатель продолжает видеть неизменный образ
файла базы данных, какой существовал в момент, когда транзакция начата.
Любые транзакции записи, которые передаются, в то время как транзакция
чтения активна, все еще невидимы для транзакции чтения, потому что читатель
видит снимок файла базы данных с предшествующего момента. Пример: Предположим, что есть два соединения с базой данных X и Y.
X запускает транзакцию чтения через BEGIN
, сопровождаемый одним или более SELECT.
Тогда Y приходит и управляет запросом UPDATE,
чтобы изменить базу данных. X может впоследствии сделать
SELECT для записей, которые изменил Y, но
X будет видеть более старые неизмененные записи, потому что изменения Y
это невидимая операция для X, пока X держит транзакцию чтения.
Если X хочет видеть изменения, которые внес Y, то X должен закончить
транзакцию и начать новую (управляя
COMMIT, а затем BEGIN). Другой пример: X запускает транзакцию чтения через
BEGIN и
SELECT, затем Y
вносит изменения базы данных, используя UPDATE
. Затем X пытается внести изменение в базу данных, используя
UPDATE. Попытка X превратить транзакцию из
чтения в запись провалится с ошибкой
SQLITE_BUSY_SNAPSHOT, так как
образ базы данных, рассматриваемый X, больше не является последней версией
базы данных. Если бы X было позволено писать, это придало бы истории форму
вилки файла базы данных, это является чем-то, что SQLite не поддерживает.
Для X, чтобы написать в базу данных, он должен сначала освободить свой снимок
(используя ROLLBACK хотя бы), затем
начать новую транзакцию через BEGIN. Если X запускает транзакцию, которая будет первоначально только читать,
но X в конечном счете захочет писать данные и не хочет быть обеспокоенным
возможными ошибками SQLITE_BUSY_SNAPSHOT, которые возникают, потому что
другая связь стоит перед ним в линии, то X может применить
BEGIN IMMEDIATE, чтобы начать
транзакцию вместо обычного BEGIN.
BEGIN IMMEDIATE идет вперед, начинает транзакцию записи и таким образом
блокирует всех других писателей. Если
BEGIN IMMEDIATE выполнена,
то никакие последующие операции в той транзакции никогда не будут терпеть
неудачу с ошибкой SQLITE_BUSY. SQLite обеспечивает изоляцию между операциями в отдельных соединениях с
базой данных. Однако, нет никакой изоляции между операциями, которые
происходят в рамках того же самого соединения с базой данных. Другими словами, если X начинает транзакцию записи, используя
BEGIN IMMEDIATE,
затем использует один или несколько UPDATE,
DELETE и/или
INSERT,
то те изменения видимы последующим SELECT,
которые оценены в соединении с базой данных X.
SELECT на другом соединении с базой данных Y
не покажут изменений, пока X не передаст транзакцию. Но
SELECT на X покажет изменения до передачи. В рамках связи единой базы данных X, оператор SELECT всегда видит все
изменения базы данных, которые закончены до начала оператора SELECT,
переданы или нейтральны. И оператор SELECT, очевидно, не видит изменений,
которые происходят после того, как оператор SELECT заканчивается.
Но что относительно изменений, которые происходят, в то время как оператор
SELECT работает? Что, если оператор SELECT начат,
sqlite3_step()
проходит через примерно половину его вывода, а некоторые
UPDATE меняют таблицу, который оператор SELECT
читает, тогда больше вызовов sqlite3_step()
сделано, чтобы закончить оператор SELECT? Будут более поздние шаги оператора
SELECT видеть изменения, внесенные UPDATE или нет?
Ответ: это поведение не определено.
В частности видит ли оператор SELECT параллельные изменения, зависит
от версии SQLite, схемы файла базы данных, выполнялся ли
ANALYZE и прочих деталей запроса.
В некоторых случаях это могло бы зависеть также от содержания файла базы
данных. Нет никакого хорошего способа знать, будет ли оператор SELECT видеть
изменения, которые были внесены в базу данных тем же самым соединением с
базой данных после того, как оператор SELECT был начат.
И следовательно, разработчики должны старательно избежать писать
запросы , которые делают предположения о том, что произойдет
при таких обстоятельствах. Если приложение выполняет SELECT на единственной таблице как
"SELECT rowid, * FROM table WHERE ...", проходит вывод запроса,
используя sqlite3_step(), исследуя каждую
строку, то безопасно для приложения удалить текущую строку
или какую-либо предшествующую, используя "DELETE FROM table WHERE rowid=?".
Также безопасно (в том смысле, что это не будет вредить базе данных)
для приложения удалить строку, которая ожидается
позже в запросе, но еще не появилась. Если будущая строка удалена,
однако, могло бы произойти, что она может все равно появиться (или нет)
даже после того, как это было предположительно удалено.
Это поведение не определено. Приложение может также INSERT новые строки в
таблицу в то время как оператор SELECT работает,
но появляются ли новые строки в последующем sqlite3_step(),
не определено. Приложение может UPDATE текущую или любую предшествующую
строку, хотя выполнение такого могло бы заставить ту строку
вновь появляться в последующем sqlite3_step().
Пока приложение готово иметь дело с этими двусмысленностями, сами операции
безопасны и не будут вредить файлу базы данных. В целях предыдущих двух параграфов два соединения с базой данных, у
которых есть тот же самый общий кэш
и которые позволили
PRAGMA read_uncommitted считаются тем же самым
соединением с базой данных. Транзакции в SQLite SERIALIZABLE. Изменения, внесенные в одном соединении с базой данных, невидимы для
всех других соединений с базой данных до передачи. Запрос видит все изменения, которые закончены на том же самом
соединении с базой данных до начала запроса, независимо от того, были ли
те изменения переданы. Если изменения происходят на том же самом соединении с базой
данных после того, как запрос запущен, но прежде чем он закончит работу,
не определено, будет ли запрос видеть те изменения. Если изменения происходят на том же самом соединении с базой данных
после того, как запрос запущен, но прежде чем он закончит работу, он
мог бы возвратить измененную строку несколько раз, или это могло бы
возвратить строку, которая была ранее удалена. В целях предыдущих четырех пунктов два соединения с базой данных,
которые используют тот же самый общий кжэш
и включили PRAGMA
read_uncommitted, считаются тем же самым соединением с базой данных, а
не отдельными соединениями с базой данных.
Choose any three.
Изоляция в SQLite
Изоляция между соединениями с базой данных
Isolation и Concurrency
Никакой изоляции между операциями на том же самом
соединении с базой данных
Итоги