Транзакции в базах данных
Определим понятие согласованного состояния базы и на его основе введем понятие транзакции как набора действий, выполняющихся как единое целое и переводящих базу из одного согласованного состояния в другое.
Изучим свойства транзакций и языковые средства для их оформления.
Покажем как транзакции обеспечивают поддержку целостности данных, параллельную работу пользователей и восстановление данных при откатах транзакций и сбоях аппаратуры.
Дадим классификацию ограничений целостности.
Введем понятие ссылочного ограничения целостности и покажем как оно поддерживается с помощью триггера.
Опишем феномены — проблемы, возникающие при параллельной работе—и на их основе определим уровни изолированности пользователей.
Изучим проблемы блокирования ресурсов и возникновения тупиков. Бегло рассмотрим многоверсионные данные.
В заключительных разделах рассмотрим роль транзакций в организации откатов и восстановлении данных после сбоев.
6.1 Зачем нужны транзакции? Что мы ждем от них? Свойства транзакций
Базы данных, на которых строятся информационные системы, должны обеспечить одновременный доступ к данным для многих, иногда сотен и тысяч, пользователей. Оказывается, при параллельной работе пользователей результаты вычислений могут зависеть от временных соотношений между действиями пользователей, от того, в какое время и в какой последовательности они выполняются. Как избежать этой зависимости? Как организовать чтение данных одним пользователем и одновременно их изменение другим? Как добиться, чтобы деньги, снятые с одного счета, и, из-за сбоя системы, не зачисленные на другой счет, не могли пропасть бесследно? Как восстановить данные при откатах и сбоях?
Все эти проблемы решаются за счет использования механизма транзакций, который должен работать в базах с любыми моделями данных.
Информационные системы, предназначенные для единственного пользователя, в настоящее время практического интереса не представляют. Но даже они нуждаются в транзакциях. Всегда существует наборы действий, такие что невыполнение некоторых из них приводит базу в противоречивое состояние. Кроме того, один пользователь может запустить несколько потоков вычислений, обращающихся, может быть, к одним данным. И тут ему потребуется механизм, обеспечивающий независимость работы этих потоков.
Транзакции обеспечивают:
- Сохранение целостности данных.
- Параллельную работу пользователей с базой данных.
- Восстановление данных при откатах и сбоях.
В этой лекции будут рассмотрены транзакции и весь круг вопросов, связанных с перечисленными тремя аспектами их применения. Сложности для начинающих возникают из-за необходимости рассмотрения во времени нескольких процессов, может быть распределенных в некоторой сети.
Пусть при выполнении двух последовательно выполняющихся действий после успешного выполнения первого из них возник сбой (рисунок 6.1).
В приведенном примере это означает, что сумма, снятая с первого счета не попала на второй счет. Хуже того, сам факт исчезновения денег никак не зафиксирован. Если вас этот неприятный факт не расстроил, и вы считаете, что можно повторить все с начала, то тут сказывается одна нехорошая привычка, вырабатываемая ориентированностью наших студентов, фактически, на научные вычисления. Скажем, складывал я две матрицы, а они у меня не сложились. Ну, бог с ними — я их сложу еще раз. Но, если вы написали систему, которая отправила миллиард долларов, и он никуда не пришел, то, скорее всего, усовершенствовать ее будет уже другой человек, а вы будете искать новую работу. Вот, собственно, в этом разница в отношении к надежности вычислений в науке и бизнесе. Вообще говоря, бизнес-приложения должны работать всегда, независимо от того, исправен ли компьютер или нет, заболели ли вы или еще что-то случилось, ночь сейчас или день. Ведь есть организации, работающие круглые сутки. Хорошим тоном считается обеспечивать работоспособность в самом тяжелом режиме, который принято называть "24*7" (двадцать четыре на семь). Это означает 24 часа в сутки 7 дней в неделю. Это очень тяжелый режим, и без специальных мер он сам по себе не обеспечивается. Представьте, что в вашем домашнем компьютере нужно поменять или добавить жесткий диск. Как это сделать без останова системы?
Для рассмотрения параллельной работы двух пользователей вспомним, что "сальдо" —это остаток на счете. Посчитать сальдо по группе счетов — это, попросту говоря, сложить их остатки. Обычный арифметический цикл, который известен всем школьникам. На рисунке 6.2 слева показана работа такого цикла. Сначала сальдо С по группе счетов равно 0, потом прибавляем к нему остаток первого счета, второго счета и т.д., счета тысячного. Как учили в школе? Написали цикл, проверили его на примере. Дальше работает всегда правильно. Но у нас то пользователь не один. Транзакция изображенная слева выполняет циклическое суммирование. Но после того, как счет 1 уже учтен, и до того, как учтен счет 1000, вторая транзакция, изображенная справа, снимает 200 руб. со счета 1, и зачисляет их (то есть прибавляет 200 руб.) на счет 1000. Теперь в подсчете сальдо по группе счетов появится ошибка в 200р. Почему? Потому что счет 1 мы обрабатывали в неизмененном состоянии, а счет 1000 в измененном. А что будет, если во времени вторая транзакция будет сдвигаться? Ну, очевидно, если она будет выполнена до первой (левой на рисунке) транзакции или после нее, то ничего страшного не случится, все будет посчитано правильно. А если транзакции начнут выполняться на одном промежутке времени то, с какого-то момента появится ошибка. Обратите внимание, довольно странная, неожиданная для начинающих ситуация — арифметический цикл, оказывается, может считать данные неправильно, если одновременно идет некоторый другой процесс, имеющий доступ к тем же данным. Транзактный механизм должен подобные казусы исключить.
В соответствии с общим принципом, на котором построен настоящий курс, мы все должны "проделать ручками", что в этом разделе я особенно вам советую, если, конечно, у вас уже нет опыта параллельных вычислений или работы с транзакциями. Параллельную работу двух пользователей моделируем включением двух терминалов Cache (рисунок 6.3). При этом транзакций мы пока не используем.
Работа с тысячью счетов в примере может затянуться, поэтому счетов у нас всего три штучки (Сч1, Сч2, Сч3). На них 500 руб., 700 руб. и 800 руб. соответственно. Сальдо С по группе этих трех счетов в начале вычисления равно нулю. Переменная t — это разметочная переменная, отмечающая моменты времени 0, 1, 2, 3, 4. Она определяет порядок действий. Сначала вы должны выполнить ту строчку в левом терминале, где есть присваивание t = 0. После него выполняете первую строчку второго терминала там, где t присваивается значение 1, и т.д. Если вам трудно следить за текстом, повторите все сами. Как и ожидалось, сальдо вычисляется с ошибкой.
Определение. База находится в согласованном состоянии, если выполняются все записанные в ней ограничения целостности. Во время выполнения транзакции база может рассогласовываться.
Вернемся к рассмотренной ранее операции — переводу денег с одного счета на другой. Эта операция включает две атомарные (элементарные, неделимые) операции:
- снятие суммы с одного счета,
- зачисление суммы на другой счет.
Если СУБД будет выполнять (или не выполнять) эти две операции только вместе, то база будет всегда находиться в согласованном состоянии и не будут возникать ситуации, при которых суммы, снятые с одного счета, ни на какой другой счет не поступают.
Определение. Объединение нескольких операций в неделимое целое называют транзакцией.
Возникает вопрос: Что делать, если часть данных уже изменена, а остальные измениться не могут? Ответ: Откатить выполненные изменения, то есть вернуться к ранее существовавшим значениям. Но как выполнить откат? Это мы выясним позже. Здесь отметим, что создание действий, аннулирующих изменения в базе, выполненные транзакцией, не приемлемо уже потому, что генерация таких инструкций и их запуск на исполнение потребовали бы слишком много времени.
При отказах информационной системы желательно сохранить сведения о попытке выполнения транзакции. Если при этом транзакция была выполнена частично, то уже выполненную часть операций необходимо откатить. После восстановления системы транзакция должна быть выполнена повторно.
6.1.1 Определение транзакции
Транзакция — это последовательность действий, выполняющаяся как единое целое и переводящая базу данных из одного согласованного состояния в другое согласованное состояние. Вы должны помнить, что ограничения целостности определенные в бизнесе, могут быть не прописаны явно. Так, в примере, приведенном на рисунке 6.1, ограничение целостности можно сформулировать так: деньги, снятые со счета, должны быть зачислены на другой счет. Может быть задействована некоторая цепочка счетов. Например, если вы отправляете деньги через свой банк клиенту другого банка, то деньги сначала поступают на ваш клиентский счет, с него — на корреспондентский счет вашего банка в другом банке, а уже оттуда на клиентский счет получателя. Всегда необходимо додумывать ситуацию до конца, тогда она будет прозрачна, понятна, и вы с ней справитесь. В транзакциях используют следующие инструкции:
- чтения данных;
- манипулирования данными;
- блокирования и разблокирования ресурсов.
Транзакции обладают набором свойств (рисунок 6.4), характеризуемым аббревиатурой АСИД (ACID):
- А - Атомарность. Транзакция выполняется как единое целое (либо все
- выполняется, либо все не выполняется).
- С - Согласованность. Транзакция переводит базу данных из одного согласованного состояния в другое согласованное состояние. Внутри транзакции согласованность базы данных может нарушаться.
- И - Изолированность. Транзакции разных пользователей не должны мешать друг другу. Иначе говоря, транзакция должна работать только с непротиворечивыми данными, не имея доступа к промежуточным результатам.
- Д - Долговечность. Результаты работы выполненной транзакции должны сохраниться в базе данных, даже если по завершении транзакции произойдет сбой системы.
Замечание. Соответствующая английская аббревиатура ACID образована первыми буквами терминов Atomicity, Consistency, Isolation, Durability.
6.1.2 Замечание о свойствах АСИД
Свойства АСИД транзакций, за исключением атомарности, не всегда выполняются в полном объеме.
- Согласованность. Нарушается внутри транзакции. При неполной изоляции транзакций несогласованные данные могут быть доступны другим транзакциям.
- Изоляция. Полной изоляции транзакций можно достигнуть, только если принудительно выстраивать транзакции в очередь и исполнять их строго одну после другой. При этом достаточно одной из транзакций зависнуть или выполняться слишком долго (сначала я пила кофе, потом меня вызвал начальник, а потом был перерыв), чтобы остановить всю работу. Поэтому вводится несколько уровней неполной изоляции.
- Долговечность. Может нарушаться если, например, транзакция , вызванная из транзакции будет завершена успешно, a откатится. Тогда откатятся данные транзакции