Изоляция в базах данных

В системах баз данных изоляция определяет, как целостность транзакции видна другим пользователям и системам. Например, когда пользователь создает заказ на покупку и создал заголовок, но не строки заказа на покупку, доступен ли заголовок для просмотра другим системам или пользователям (выполняющим параллельные операции, например, отчет по заказам на покупку)?

Более низкий уровень изоляции увеличивает возможность доступа многих пользователей к одним и тем же данным в одно и то же время, но увеличивает количество эффектов параллелизма (таких как грязное чтение (dirty reads) или потерянные обновления (lost updates)), с которыми могут столкнуться пользователи. И наоборот, более высокий уровень изоляции уменьшает типы эффектов параллелизма, с которыми могут столкнуться пользователи, но требует больше системных ресурсов и увеличивает вероятность того, что одна транзакция заблокирует другую.

Изоляция обычно определяется на уровне базы данных как свойство, которое определяет, как и когда изменения, сделанные одной операцией, становятся видимыми для других. В старых системах это может быть реализовано системно, например, за счет использования временных таблиц. В двухуровневых системах диспетчер обработки транзакций (TP) необходим для поддержания изоляции. В многоуровневых системах (например, несколько веб-сайтов, пытающихся забронировать последнее место в рейсе) требуется сочетание хранимых процедур и управления транзакциями для подтверждения бронирования и отправки подтверждения клиенту.

Изоляция (Isolation) - одно из четырех свойств ACID, наряду с (атомарностью (atomicity), последовательностью (consistency) и долговечностью (durability)).

Контроль конкурентности

Контроль конкурентности включает в себя базовые механизмы СУБД, которые обрабатывают изоляцию и гарантируют соответствующую корректность. Он активно используется СУБД и механизмами хранения как для обеспечения правильного выполнения конкурентных транзакций, так и (с помощью различных механизмов) правильности других процессов СУБД. Связанные с транзакциями механизмы обычно ограничивают время операций доступа к данным базы данных (расписания транзакций) определенными порядками, которые характеризуются свойствами графика сериализуемости и восстанавливаемости. Ограничение выполнения операции доступа к базе данных обычно означает снижение производительности (измеряемое скоростью выполнения), и, таким образом, механизмы управления конкурентностью обычно предназначены для обеспечения наилучшей производительности, возможной в условиях ограничений. Часто, когда это возможно без ущерба для правильности, свойство сериализуемости ухудшается для повышения производительности. Однако возможность восстановления не может быть нарушена, поскольку это обычно приводит к быстрому нарушению целостности базы данных.

Двухфазная блокировка - это наиболее распространенный метод управления конкурентностью транзакций в СУБД, используемый для обеспечения как сериализуемости, так и возможности восстановления для корректности. Чтобы получить доступ к объекту базы данных, транзакция сначала должна получить блокировку для этого объекта. В зависимости от типа операции доступа (например, чтение или запись объекта) и от типа блокировки получение блокировки может быть заблокировано и отложено, если другая транзакция удерживает блокировку для этого объекта.

Феномены чтения

Стандарт ANSI/ISO SQL 92 относится к трем различным феноменам чтения, когда транзакция 1 считывает данные, которые транзакция 2 могла изменить.

В следующих примерах имеют место две транзакции. В первой выполняется запрос 1. Затем во второй транзакции выполняется и фиксируется запрос 2. Наконец, в первой транзакции снова выполняется запрос 1.

Запросы используют следующую таблицу данных:

users
id name age
1 Joe 20
2 Jill 25

Грязные чтения

Грязное чтение (dirty read) (также известное как незафиксированная зависимость (uncommitted dependency)) происходит, когда транзакции разрешено читать данные из строки, которая была изменена другой запущенной транзакцией и еще не зафиксирована.

Грязное чтение работает аналогично неповторимому чтению; однако вторую транзакцию не требуется фиксировать, чтобы первый запрос возвратил другой результат. Единственное, что можно предотвратить на уровне изоляции READ UNCOMMITTED, - это неправильное отображение обновлений в результатах; то есть более ранние обновления всегда будут появляться в наборе результатов перед более поздними обновлениями.

В нашем примере транзакция 2 изменяет строку, но не фиксирует изменения. Затем транзакция 1 считывает незафиксированные данные. Теперь, если транзакция 2 откатывает свои изменения (уже прочитанные транзакцией 1) или обновляет различные изменения в базе данных, тогда представление данных может быть неправильным в записях транзакции 1.

Транзакция 1 Транзакция 2

/* Запрос 1 */
SELECT age FROM users WHERE id = 1;
/* будет прочитано 20 */

/* Запрос 2 */
UPDATE users SET age = 21 WHERE id = 1;
/* коммита нет */

/* Запрос 1 */
SELECT age FROM users WHERE id = 1;
/* будет прочитано 21 */

ROLLBACK;
/* основанное на блокировке
грязное чтение (DIRTY READ) */

Но в этом случае не существует строки с id 1 и age 21.

Неповторяющиеся чтения

Неповторяющееся чтение происходит, когда в ходе транзакции строка извлекается дважды, а значения в строке различаются между чтениями.

Явление неповторяющихся чтений может возникать в методе управления конкурентностью на основе блокировок, когда блокировки чтения не устанавливаются при выполнении SELECT или когда полученные блокировки для затронутых строк снимаются сразу после выполнения операции SELECT. При использовании метода управления многоверсионным конкурентностью неповторяющиеся чтения могут происходить, когда требование отката транзакции, затронутой конфликтом фиксации, ослаблено.

Транзакция 1 Транзакция 2

/* Запрос 1 */
SELECT * FROM users WHERE id = 1;

/* Запрос 2 */
UPDATE users SET age = 21 WHERE id = 1;
COMMIT;
/* в многоверсионном контроле конкурентности,
или на основе блокировки READ COMMITTED */

/* Запрос 1 */
SELECT * FROM users WHERE id = 1;
COMMIT;
/* на основе блокировки REPEATABLE READ */

В этом примере транзакция 2 успешно фиксируется, что означает, что ее изменения в строке с идентификатором 1 должны стать видимыми. Однако транзакция 1 уже увидела другое значение age в этой строке. На уровнях изоляции SERIALIZABLE и REPEATABLE READ СУБД должна возвращать старое значение для второго SELECT. При READ COMMITTED и READ UNCOMMITTED СУБД может вернуть обновленное значение; это неповторимое чтение.

Есть две основные стратегии, используемые для предотвращения неповторяющихся чтений. Первая - отложить выполнение транзакции 2 до тех пор, пока транзакция 1 не будет зафиксирована или откачена. Этот метод используется при использовании блокировки и производит последовательное расписание T1, T2. Последовательное расписание демонстрирует повторяющееся поведение чтения.

В другой стратегии, используемой в управлении конкурентностью с несколькими версиями, транзакция 2 может быть зафиксирована первой, что обеспечивает лучшую конкурентность. Однако транзакция 1, которая началась до транзакции 2, должна продолжать работать с предыдущей версией базы данных - моментальным снимком того момента, когда она была запущена. Когда транзакция 1 в конечном итоге пытается выполнить фиксацию, СУБД проверяет, будет ли результат фиксации транзакции 1 эквивалентен расписанию T1, T2. Если это так, то транзакция 1 может продолжаться. Однако, если он не может быть признан эквивалентным, транзакция 1 должна откатиться с ошибкой сериализации.

При использовании метода управления конкурентностью на основе блокировки в режиме изоляции REPEATABLE READ строка с ID = 1 будет заблокирована, что приведет к блокировке запроса 2 до тех пор, пока первая транзакция не будет зафиксирована или откачена. В режиме READ COMMITTED при втором выполнении запроса 1 age изменился.

При мультиверсионном управлении конкурентностью на уровне изоляции SERIALIZABLE оба запроса SELECT видят моментальный снимок базы данных, сделанный в начале транзакции 1. Следовательно, они возвращают одни и те же данные. Однако, если затем транзакция 2 попытается выполнить UPDATE этой строки, произойдет сбой сериализации, и транзакция 1 будет вынуждена откатиться.

На уровне изоляции READ COMMITTED каждый запрос видит моментальный снимок базы данных, сделанный в начале каждого запроса. Таким образом, каждый из них видит разные данные для обновленной строки. В этом режиме невозможен сбой сериализации (поскольку не дается никаких обещаний о сериализуемости), и транзакцию 1 не нужно будет повторять.

Фантомное чтение

Фантомное чтение происходит, когда в ходе транзакции новые строки добавляются или удаляются другой транзакцией к читаемым записям.

Это может произойти, если при выполнении операции SELECT ... WHERE не выполняется блокировка диапазона. Аномалия фантомного чтения является особым случаем неповторяемых чтений, когда транзакция 1 повторяет ранжированный запрос SELECT ... WHERE и между обеими операциями транзакция 2 создает (т.е. INSERT) новые строки (в целевой таблице), которые выполняют это WHERE пункт.

Транзакция 1 Транзакция 2

/* Запрос 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;

/* Запрос 2 */
INSERT INTO users(id,name,age) VALUES ( 3, 'Bob', 27 );
COMMIT;

/* Запрос 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
COMMIT;

Обратите внимание, что транзакция 1 дважды выполнила один и тот же запрос. Если поддерживался наивысший уровень изоляции, оба раза должен возвращаться один и тот же набор строк, и это действительно то, что должно происходить в базе данных, работающей на уровне изоляции SQL SERIALIZABLE. Однако на меньших уровнях изоляции другой набор строк может быть возвращен во второй раз.

В режиме изоляции SERIALIZABLE запрос 1 приведет к блокировке всех записей с age в диапазоне от 10 до 30, поэтому запрос 2 будет блокироваться до тех пор, пока не будет зафиксирована первая транзакция. В режиме REPEATABLE READ диапазон не будет заблокирован, что позволит вставить запись и второе выполнение запроса 1 для включения новой строки в результаты.


Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Хэш-таблица: разрешение коллизий

Нормальные формы, пример нормализации в базе данных