Лабораторная работа №4
«Уровни изоляции транзакций. Точки сохранения.»
Уровень изоляции - это степень видимости внутри транзакции изменений, выполненных за пределами транзакции. Он определяет насколько чувствительна транзакция к изменениям, выполненными другими транзакциями. Например, если в SQL Server две транзакции запущены независимо одна от другой, то по умолчанию записи, вставленные одной транзакцией, не видны в другой до тех пор, пока первая не будет зафиксирована.
Концепция уровней изоляции тесно связана с концепцией блокировок: определив уровни изоляции, вы определяете как долго эта транзакция будет блокировать ресурсы, чтобы защищать их от изменений, которые могут быть сделаны другими операциями.
Термины :
Грязное чтение(dirty read) – это ситуация, когда транзакция читает данные, которые еще не зафиксированы другой транзакцией.
Невоспроизводимое чтение (nonrepeatable read). Например транзакция А читает запись таблицы. После этого транзакция В изменяет или удаляет запись и фиксирует свои изменения. Если теперь транзакция А попытается снова прочитать ту же запись, то в ней будут другие данные или запись вообще будет не доступна. Такое состояние называется невоспроизводимым чтением.
Фантомное чтение (phantom read) пусть у транзакции А есть некий критерий выборки, например, есть 100 строк, удовлетворяющих этому критерию. Затем транзакция В вставляет еще несколько строк, удовлетворяющих тоже этому критерию. Если транзакция А снова выполнит запрос выборки, то она получит набор строк, отличающихся от предыдущего. Строки, добавленные таким образом, называются фантомными строками.
Уровни изоляции, возможные в ADO.NET
Эти значения входят в перечисление IsolationLevel.
Chaos. Ожидающие записи изменения транзакций более высоких уровней изоляции не могут быть перезаписаны. Этот параметр не поддерживается в SQL Server и вOracle.
ReadIncommitted. Разрешается грязное чтение. Такой уровень изоляции применяется когда, не нужна гарантированная точность выбираемых данных, и скорость важнее точности.
ReadСommitted. Пока транзакция читает данные, удерживаются разделяемые блокировки. Это позволяет избежать грязного чтения, но данные могут быть изменены до завершения транзакции. В результате может возникнуть невоспроизводимое чтение или фантомные строки. Этот уровень удобен, когда нужно работать со всеми данными, которые удовлетворяют определенным условиям и зафиксированы.
Разделяемая блокировка (shared lock) – это блокировка, которая устанавливается, когда транзакции нужно прочитать данные из базы, и на эти данные не установлена монопольная блокировка. Монопольная блокировка – это блокировка, которая предотвращает изменение данных одновременно двумя или более транзакциями. Она применяется, когда транзакции необходимо обновить таблицу и к ней не применимы другие блокировки. Данный уровень изоляции может быть использован, когда необходима транзакционная согласованность длительно выполняющихся запросов и допустима возможность невоспроизводимого чтения.
RepeatableRead. Разделяемые блокировки устанавливаются на все данные, используемые в критерии запроса. Этот уровень изоляции используется, когда нужно, чтобы прочитанные записи содержали те же данные и в последующих операциях чтения. Этот вид удобен, когда необходима целостность данных внутри транзакции за счет снижения производительности параллельной работы системы.
Snapshot. Этот уровень изоляции уменьшает вероятность установки блокировки строк, сохраняя копию данных, которые одно приложение может читать, в то время как другое модифицирует эти же данные. Этот уровень используется в приложениях, которым необходима целостность данных при длительно выполняющихся запросах, но они не собираются модифицировать данные. Эта изоляция в SQL Server должна быть разрешена на уровне самой базы. Это можно сделать с помощью следующей команды:
ALTER DATABASE <<имя базы данных>> SET ALLOW_SNAPSHOT_ISOLATION ON
Serializable. В этом случае данные блокируются, что предотвращает обновление или вставку сток в DataSet другими пользователями до тех пор, пока транзакция не завершится. Эту блокировку можно установить на строку или таблицу. Она удобна когда нужно, чтобы все данные задействованные в условии или выражении оставались неизменными до окончания обработки.
Unspecified. При этом типе используется уровень изоляции, отличный от указанного, однако его невозможно определить.
Эти значения уровня изоляции могут быть заданы в процессе инициализации транзакции методом BeginTransaction. Важно знать, что будет ли работать конкретный уровень изоляции для данного источника данных, зависит от соответствующей СУБД. Узнать значение уровня изоляции можно с помощью свойства IsolationLevel объекта транзакции.
Изменение уровней изоляции
Разработаем приложение, которое изменяет стандартный уровень изоляции СУБД SQL Server ReadCommitted на ReadUncommitted. Принцип работы приложения:
Открывается подключение к локальной базе данных Universitet и начинается транзакция. Уровень изоляции этой транзакции устанавливается по умолчанию ReadCommitted.
Открывается еще одно подключение к базе данных и начинается еще одна транзакция. Но для этой транзакции устанавливается уровень изоляции ReadUncommitted.
Из первой транзакции вставляется строка в таблицу Kafedras
Без фиксации первой транзакции из второй выполняется чтение этой строки с уровнем изоляции ReadUncommitted. Результаты выводятся на консоль
Первая транзакция откатывается, и из второй запускается тот же запрос, чтобы выбрать только что вставленную запись.
**
using System. Data. SqlClient;
namespace Lab_trans
{
class Program
{ static void Main(string[] args)
{
SqlConnection con1 = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=D:\universitet. mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
SqlConnection con2 = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=D:\universitet. mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
con1.Open();
con2.Open();
SqlTransaction trans1 = con1.BeginTransaction(); SqlTransaction trans2 =con2.BeginTransaction(IsolationLevel. ReadUncommitted);
SqlDataReader dr;
try
{
SqlCommand cmd1 = new SqlCommand("INSERT INTO Kafedrs(IDKafedra, Kafedra)VALUES(@ID,@Kaf)", con1, trans1);
cmd1.Parameters. AddWithValue("@ID", "7");
cmd1.Parameters. AddWithValue("@Kaf", "ПЯВУ");
cmd1.ExecuteNonQuery();
SqlCommand cmd2 = new SqlCommand("select * from Kafedrs where IDKafedra =7", con2, trans2);
dr = cmd2.ExecuteReader();
Console. WriteLine("Результаты при частично выполненной транзакции:");
if (!dr. HasRows)
{ Console. WriteLine("строки не найдены"); Console. ReadKey(); }
while (dr. Read())
{
Console. WriteLine("Номер: " + dr[0] + " Название:" + dr[1]);
}
Console. ReadKey();
dr. Close();
trans1.Rollback();
cmd2.CommandText = "select * from Kafedrs where IDKafedra =7";
dr = cmd2.ExecuteReader();
Console. WriteLine("Результаты после отката транзакции:");
if (!dr. HasRows)
{ Console. WriteLine("строки не найдены"); Console. ReadKey(); }
while (dr. Read())
{
Console. WriteLine("Номер: " + dr[0] + " Название:" + dr[1]);
Console. ReadKey();
}
dr. Close();
}
catch (System. Exception e)
{
Console. WriteLine(e. ToString()); Console. ReadKey();
}
finally
{
con1.Dispose(); //закрывает соединение
con2.Dispose();
}
Если сделать модификацию программы и значение уровня изоляции изменить на Serializable, а затем запустить программу снова, то вы увидите, что она зависнет на выполнении команды SELECT. Это происходит потому, что этот уровень изоляции блокирует все дальнейшие чтения из источника данных, пока первая транзакция не завершится.
Теперь установите уровень изоляции обеих транзакций в Snapshot и вновь запустите приложение. Должно появиться сообщение об исключении.
SQL Server 2005позволяет запускать несколько команд по одному подключению. Эта возможность называется «Множественные активные наборы результатов» или MARS.
Рассмотрим пример, использования MARS.
SqlConnection con1 = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=D:\universitet. mdf;Integrated Security=True;Connect Timeout=30;User Instance=True; ");
con1.Open();
SqlCommand cmd1 = con1.CreateCommand();
SqlCommand cmd2 = con1.CreateCommand();
cmd1.CommandText = "select * from Kafedrs";
cmd2.CommandText = "update Kafedrs set Kafedra ='ПОВТ'where IDKafedra = @ID";
SqlParameter id= cmd2.Parameters. Add("@ID",SqlDbType. SmallInt);
SqlDataReader dr = cmd2.ExecuteReader();
while (dr. Read())
{
id. Value=dr. GetInt16(0);
cmd2.ExecuteNonQuery();
***
}
dr. Close();
con1.Dispose();
Но при попытке запуска получите исключение:
There is already an open DataReader associated with this Command which must be closed first.
Чтобы код заработал необходимо, необходимо использовать MARS, для этого необходимо в строку подключения добавить параметр: MultipleActiveResultsets=true;
MARS выполняет команды не параллельно, а чередуя их. Все команды можно поделить на две группы по признаку чередования.
Первая группа содержит команды, позволяющие выполнение с чередованием до их окончания. Это SELECT, READTEXT, RECEIVE и др.
Во вторую группу входят такие команды как UPDATE, INSERT и др.
Чтобы использовать MARS в OLEDB, в строку подключения необходимо добавить следующий параметр:
MarsConn = yes
А при подключении с использованием компонентов службы OleDb нужен параметр
MARS Connection = True
Данная технология позволяет запускать несколько чередующихся команд, но не позволяет одновременно выполнять несколько транзакций по одному подключению. Но можно выполнять чередующиеся команды в одной транзакции по одному подключению.
MARS позволяет установить точки сохранения, если команды выполняются последовательно, и запрещает, если команды могут чередоваться.
Точки сохранения
При откате транзакции происходит аннулирование эффекта каждой операции транзакции. В некоторых случаях не требуется отменять все операции – это реализуется с помощью точек сохранения.
Точки сохранения (savepoint)– это маркеры, выполняющие роль закладок. Во время выполнения транзакции можно поместить какую – либо точку, и затем выполнить откат к этой точке вместо полного отката всей транзакции.
Для этого предназначен метод Save объекта транзакции, он имеется только в классе SqlTransaction.
Пример приложения, которое иллюстрирует использование метода Save.

Обратите внимание, что в результате будут отсутсвовать некоторые идентификаторы кафедр из – за отката некоторых вставок.
Примечание: после выполнения отката к одной из точек сохранения все точки сохранения, установленные за этой точкой, теряются. Если они нужны, их придется устанавливать заново.
Вложенные транзакции
Вложение транзакций дает возможность организовать иерархию откатов. Для запуска вложенных транзакций используется метод Begin класса транзакции, он доступен только для класса OleDbTransaction.
Например,
Mytransaction = con. BeginTransaction();
Myanothertransaction = Mytransaction. Begin ()
Метод Begin возвращает экземпляр другого объекта транзакции, который можно использовать так же, как и первоначальный объект транзакции. Но откат этой транзакции просто откатывает текущую транзакцию, но не всю в целом.
САМОСТОЯТЕЛЬНО:
Создайте приложение, соединяющееся с базой Access, использующее вложенные транзакции для отката действий.
КОНТРОЛЬНЫЕ ВОПРОСЫ:
Что такое уровни изоляции? Что такое грязное чтение, фантомное чтение, невоспроизводимое чтение? Какие бывают уровни изоляции и когда их лучше применять? Как установить уровень изоляции? Что такое технология MARS, для чего и как ее использовать? Что такое точки сохранения? Как установить точки сохранения? Почему надо применять вложенные транзакции при обращении к базе данных Access? Как создать вложенную транзакцию?

