Россия |
Транзакции в базах данных
6.4 Транзакции и параллельная работа
Любая система в бизнесе представляет множество действующих лиц — акторов — работающих одновременно и организованно. Актором может быть человек, программа или машина. Информационная система должна как-то отражать эту многоакторность. Конечно, в ней самой могут появиться параллельные процессы, не имеющие прямых аналогов в бизнесе.
В этом разделе будут рассмотрены проблемы, возникающие при параллельной работе транзакций. В соответствии с традицией будем называть эти проблемы феноменами. На основе феноменов зададим уровни изолированности транзакций. В определениях изолированности будет использован стандарт версии SQL-92 языка SQL, но знание SQL и, тем более, стандарта для освоения материала не требуется.
В заключительной части раздела рассмотрим блокировки ресурсов и эффекты, возникающие при блокировании.
6.4.1 Феномены
Феномен "Потерянные изменения"
При отсутствии блокировок или других средств, ограничивающих доступ к ресурсам, используемым транзакцией, некоторые изменения могут быть утеряны.
Пример (таблицы 6.4 и (6.5): На счет Acc с начальным сальдо 100, зачисляют первой транзакцией 20 руб., а второй 100 руб. Если первая транзакция записывает измененные данные в базу и завершается после того, как вторая транзакция их прочла, но до того, как она их записала, то изменения, внесенные первой транзакцией, теряются.
Транзакция 1 | Время | Транзакция 2 |
Начало Тр1 | ||
Чтение X=Acc | to | |
Начало Тр2 | ||
ti | Чтение X=Acc | |
t2 | Зачисление X=X+100 | |
Зачисление X=X+20 | t3 | |
Запись в базу Acc=X | t4 | |
COMMIT | ||
Конец Тр1 | ||
t5 | Запись в базу Acc=X COMMIT | |
Конец Тр2 |
USER>TStart USER>Set Х=^Асс, t=Q USER>Set Х=Х+20, t = 3 USER>Set ^Асс=Х, t = 4 USER>TCommit USER> USER>TStart |
USER>Set Х=^Асс, t=1 USER>Set X=X+100, t=2 USER>Set ^Acc=X, t = 5 U5ER>TComoit USER>Write ^Acc 2 00 USER>| |
Начальное значение ^Acc=100. TSTART — начало транзакции, TCOMMIT — ее успешное завершение. Переменная t, как и раньше, использована вместо комментария для задания отметки очередности исполнения команд, соответствующей моментам времени .
Зависимость от незафиксированных результатов (чтение "грязных" данных)
Феномен возникает, когда читаются данные незавершенной транзакции, которые впоследствии откатываются. Начальное значение Acc в примере равно 100 (таблицы 6.6 и 6.7).
Транзакция 1 | Время | Транзакция 2 |
Начало Тр1 | ||
Изменение Acc=Acc+20 | t1 | |
Начало Тр2 | ||
t2 | Чтение и изменение | |
X=Acc+50 | ||
ROLLBACK —возврат к Acc=100 | tз | |
Конец Тр1 | ||
t4 | Acc=X | |
Конец Тр2. Значение Acc не 150, а 170 |
USER>TStart USER>Set ^Acc=^Acc+20, t=1 USER>TRollback USER>Sett=3 Rollback Write ^Acc 100 USER> |
USER>TStart USER>S X=^Acc+50, t=2 USER>Set ^Acc=X ,t=4 USER>TCommit USER>Set t=5 USER>Write ^Acc 170 |
Феномен "Неповторяющееся чтение"
Разберите сами приведенный ниже пример (таблицы 6.8 и 6.9).
Транзакция 1 | Время | Транзакция 2 |
Начало Тр2 | ||
t1 | Чтение Acc (Acc=100) | |
Начало Тр1 | ||
Изменение Acc=300 | t2 | Проверка Acc<200 — Да |
t3 | Чтение Acc (Acc=300) Проверка Acc<200 — Нет |
USER>TStart U5ER>Set t=2, ^Acc=300 USER> |
USER>TStart USER<Set t=l USER>If ^Acc<200 Write "Да"Да USER>Set t=3U SER>If ^Acc<200 Write "Да" USER>| |
Феномен появления записей "фантомов"
Ниже мы изучим блокировки, то есть защиту от попыток совместного использования записей. За счет блокировок на уровне строк перечисленные выше три феномена могут быть исключены. Но останется еще одна проблема.
Пусть транзакция Тр1 выбирает группу строк из таблицы Т, удовлетворяющих условию У1. Затем транзакция Тр2 вставляет в Т еще одну строку, удовлетворяющую условию У1. При повторной выборке строк транзакцией Тр1, к полученному в предыдущей выборке набору добавляется еще одна строка, называемая фантомом, привидением.
6.4.2 Уровни изолированности пользователей
Стандарт SQL-92, основываясь на перечисленных феноменах, определяет четыре уровня изолированности:
- Read uncommitted. Чтение незафиксированных изменений. Самый низкий уровень изоляции. Воспринимаются как окончательные, так и промежуточные результаты других транзакций. СУБД предотвращает только проблему потерянных изменений.
- Read committed. Чтение зафиксированных изменений. Уровень изоляции выше, чем у read uncommited. Транзакция не имеет доступа к промежуточным результатам других транзакций. Но окончательные результаты других транзакций, завершившихся во время исполнения нашей транзакции, могут быть доступны. Возможно получение
- разных результатов при повторе запроса до и после фиксации другой транзакции, изменяющей данные. Возможны фантомы.
- Repeatable read. Повторяемое чтение. Уровень изоляции выше, чем у read commited. Транзакция не имеет доступа к промежуточным или окончательным результатам других транзакций. Вставки записей приводят к появлению фантомов.
- Serializable. Сериализуемость. Самый высокий уровень изоляции. Результат параллельного выполнения транзакций будет точно таким же как при последовательном их выполнении. Все перечисленные выше феномены отсутствуют, но параллельное выполнение транзакций, работающих с одними ресурсами невозможно.
Данные по уровням изолированности сведены в таблицу 6.10. Знак "+" в ней означает наличие феномена, а знак "-" — его отсутствие.
Уровень | Потерянные | "Грязное" | Неповторяющееся | Фантомы |
изоляции | изменения | чтение | чтение | |
Serializable | + | + | + | + |
Repeatable read | + | + | + | - |
Read commited | + | + | - | - |
Read uncommited | + | - | - | - |
Так зачем нужны "плохие" виды транзакций, и особенно, Read uncommited?
Ответ простой. С одной стороны, чем выше уровень изоляции, тем меньше шансов на параллельную работу транзакций. С другой стороны, полная изоляция не всегда полезна.
Пример: Библиотекари регулярно работают с каталогом, открывая транзакции для сверки и изменения каталога. Эти работы могут длиться часами. Что Вы предпочитаете, ждать появления исправленного каталога, или просматривать его в любой момент времени, может быть, иногда получая неправильные данные?
Замечание. Правильнее было бы дать библиотекарям работать с копией каталога, а когда они закончат работу быстро обновить каталог, запретив остальным пользователям доступ на несколько минут или секунд.
Как обеспечить правильную работу транзакций, обращающихся к одним и тем же ресурсам?
Два основных способа:
- блокирование "общих" ресурсов;
- предоставление транзакциям, конкурирующим за ресурсы, разных экземпляров данных.