45808 (Блокировки в MS SQL Server 2000)

2016-07-31СтудИзба

Описание файла

Документ из архива "Блокировки в MS SQL Server 2000", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "45808"

Текст из документа "45808"

Блокировки в MS SQL Server 2000

Алексей Ширшов

Введение

Обычно блокировки рассматривают совместно с транзакциями. В данной статье упор делается в основном на механизм блокировок, его внутреннее устройство и использование в СУБД MS SQL Server 2000. Предполагается, что читатель хорошо знаком с транзакциями и их свойствами. Давайте вспомним вкратце, какими свойствами должны обладать транзакции в современных СУБД (эти требования носят название ACID – Atomicity, Consistency, Isolation и Durability):

Atomicity (атомарность). Это требование заключается в том, что все данные, с которыми работает транзакция, должны быть либо подтверждены (commit), либо отменены (rollback). Не должно быть ситуации, когда часть изменений подтверждается, а часть – отменяется. Это правило автоматически выполняется для простых данных.

Consistency (согласованность). После выполнения транзакции все данные должны остаться в согласованном состоянии. Другими словами, транзакция либо не изменит данных, и они останутся в прежнем состоянии, либо измененные данные будут удовлетворять ограничениям целостности, правилам (rules) и другим критериям согласованности данных.

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

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

В данной статье рассматриваются механизмы реализации уровней изолированности транзакции. Стандартом ANSI были определены четыре уровня изоляции транзакций. Первый – это нулевой уровень изоляции, второй – первый уровень и так далее. Эти уровни помогают решать различные проблемы, которые будут рассматриваться подробно далее в процессе написания демонстрационной программы на С++. Определения уровней будут даны в конце раздела.

Итак, чтобы лучше понять проблемы изоляции транзакций, рассмотрим их сначала с точки зрения программирования на С++. Так как наша программа будет оперировать простыми данными (значение типа int), будем считать, что требования атомарности выполняются автоматически. Кроме того, мы не будем налагать каких-либо логических ограничений на значение переменной и не будем использовать вложенных транзакций, так что требования согласованности и устойчивости также будут опущены.

Наша программа содержит всего два класса: CObject и CProxy. Класс CObject - это объект-одиночка (singleton), который содержит переменную value (доступ к этой переменной мы и будем защищать), и некоторый набор служебных функций. Класс CProxy представляет собой посредника для объекта CObject; именно с ним будет работать клиент. Вот первоначальный набросок (в классе CProxy используется нестандартная конструкция __declspec(property), поддерживаемая только компиляторами от Microsoft):

class CObject;

class CProxy

{

friend class CObject;

public:

__declspec(property(get=get_Value,put=put_Value)) int value;

int get_Value(int level = -1) const;

void put_Value(int i);

void Commit();

void Rollback();

private:

int _level;

int _value;

bool fUpd;

CProxy(CObject* par,int level)

{

fUpd = false;

parent = par;

_level = level;

}

CObject* parent;

};

class CObject

{

friend class CProxy;

public:

static CProxy& GetObject(int level = -1);

~CObject()

{

if (hMutex) CloseHandle(hMutex);

}

protected:

CProxy& BeginTran(int level)

{

return *(new CProxy(this,level));

}

void RequestExclusive(int level)

{

}

void RequestShared(int level)

{

}

void RemoveShared(int level)

{

}

void RemoveLocks()

{

}

private:

CObject()

{

value = 0;

}

int value;

static HANDLE hMutex;

};

__declspec(selectany) HANDLE CObject::hMutex = NULL;

CProxy& CObject::GetObject(int level)

{

HANDLE hLocMutex = CreateMutex(NULL,TRUE,_T("Guard-Lock-Mutex"));

bool flg = GetLastError() == ERROR_ALREADY_EXISTS;

if (flg) WaitForSingleObject(hLocMutex,INFINITE);

else CObject::hMutex = hLocMutex;

static CObject obj;

ReleaseMutex(hLocMutex);

if (flg) CloseHandle(hLocMutex);

return obj.BeginTran(level);

}

void CProxy::Commit()

{

parent->RemoveLocks();

delete this;

}

void CProxy::Rollback()

{

if (fUpd)

parent->value = _value;

parent->RemoveLocks();

delete this;

}

void CProxy::put_Value(int i)

{

parent->RequestExclusive(_level);

if (!fUpd)

_value = parent->value;

parent->value = i;

fUpd = true;

}

int CProxy::get_Value(int level) const

{

if (level == -1)

level = _level;

parent->RequestShared(level);

int v = parent->value;

parent->RemoveShared(level);

return v;

}

Клиент никогда не имеет дела непосредственно с экземпляром класса CObject. Экземпляры класса CProxy – представляют копию данных объекта CObject и делегируют запросы на чтение и запись переменной value. Код получился несколько громоздким: к чему такие сложности? Я заранее определил довольно широкий интерфейс, чтобы потом меньше исправлять. :)

Прошу обратить внимание на довольно сложный механизм создания экземпляра CObject в функции GetObject. Обычно в программах используется более простой код, типа:

CProxy& CObject::GetObject(int level)

{

static CObject obj;

return obj.BeginTran(level);

}

Чем он плох? Дело в том, что если несколько потоков попытаются одновременно вызвать функцию GetObject, конструктор класса CObject может быть вызван более одного раза, так как компилятор (возможно, это его ошибка) не генерирует безопасный код проверки с использованием ассемблерной инструкции cmpxchg. Хотя вероятность возникновения такой ситуации довольно низка, я рекомендую все же не игнорировать ее. Самое простое решение проблемы заключается в использовании недорогого ресурса критической секции, например, так:

CProxy& CObject::GetObject(int level)

{

::EnterCriticalSection(&g_cs);

static CObject obj;

::LeaveCriticalSection(&g_cs);

return obj.BeginTran(level);

}

Однако встает вопрос: где ее инициализировать? Можно в конструкторе глобального объекта, но если у нас будет такой же глобальный клиент, мы не сможем гарантировать, что инициализация критической секции произойдет раньше вызова функции GetObject. Нам нужно что-то, что создается, инициализируется и захватывает ресурс непосредственно в функции GetObject. В качестве этого «чего-то» я выбрал объект исполнительной системы «Мьютекс». Его использование вы и можете наблюдать в первоначальном коде.

Теперь рассмотрим пример с использованием этих классов, который сразу вскрывает первую проблему.

unsigned __stdcall thread_proc(void*)

{

// Начало транзакции

CProxy& prx = CObject::GetObject();

prx.value = 20;

prx.Commit();

return 0;

}

int main(int argc, char* argv[])

{

// Начало транзакции

CProxy& prx = CObject::GetObject();

prx.value = 10;

// Начало новой сессии

_beginthreadex(0,0,thread_proc,0,0,0);

// Эмулируем работу

// Sleep(1000);

printf("%d\n",prx.value);

prx.Commit();

return 0;

}

Здесь я в двух параллельных потоках изменяю значение переменной value объекта CObject: в одном – на 10, во втором – на 20. Что выведется на консоль? Определенно сказать нельзя: если раскомментировать строчку Sleep(1000), выведется 20. С закомментированной строчкой выводится 10. Эта проблема носит название «проблема потери последнего изменения» (lost update problem) или проблема «грязной» записи. Она заключается в том, что при одновременном выполнении транзакций, в которых производится изменение данных, невозможно сказать заранее, какое конечное значение примут данные после фиксирования обеих транзакций. В случае «грязной» записи только одна из всех параллельно выполняющихся транзакций будет работать с действительными данными, остальные – нет. Другими словами, хотя данные и будут находиться в согласованном состоянии, логическая их целостность будет нарушена.

Для того чтобы наш объект удовлетворял первому уровню изоляции транзакций, на котором запрещается «загрязнение» данных, перепишем его следующим образом (изменения касаются только класса CObject):

class CObject

{

friend class CProxy;

public:

enum {READ_UNCOMMITTED};

static CProxy& GetObject(int level = -1);

~CObject()

{

DeleteCriticalSection(&exclusive);

if (hMutex) CloseHandle(hMutex);

}

protected:

CProxy& BeginTran(int level)

{

return *(new CProxy(this,level));

}

void RequestExclusive(int level)

{

if (level >= READ_UNCOMMITTED)

TestExclusive();

}

void RequestShared(int level)

{

}

void RemoveShared(int level)

{

}

void RemoveLocks()

{

RemoveAllLocks();

}

private:

CObject()

{

value = 0;

InitializeCriticalSection(&exclusive);

}

void TestExclusive()

{

//Проверка на монопольную блокировку

EnterCriticalSection(&exclusive);

//Вошли больше одного раза

if (exclusive.RecursionCount > 1)

LeaveCriticalSection(&exclusive);

}

void RemoveAllLocks()

{

//Если была установлена монопольная блокировка - снимаем

if (exclusive.OwningThread == (HANDLE)GetCurrentThreadId())

LeaveCriticalSection(&exclusive);

}

int value;

CRITICAL_SECTION exclusive;

static HANDLE hMutex;

};

Добавленный код выделен. Хочется пояснить одну деталь: так как изменять значение переменной value можно несколько раз, а Commit (или Rollback) вызывать только раз, налицо тот факт, что функция EnterCriticalSection вызывается бОльшее количество раз, нежели LeaveCriticalSection. Это ошибка – в соответствии с документацией количество вызовов функций EnterCriticalSection и LeaveCriticalSection должно совпадать. Поэтому после входа в критическую секцию я проверяю поле RecursionCount, которое устанавливает количество повторных входов потока в критическую секцию.

ПРИМЕЧАНИЕ

При работе под ОС семейства Windows 9x это поле не используется и всегда содержит 0, так что приводимый здесь и далее код будет работать только на операционных системах семейства NT.

Теперь можно определенно сказать, что выведется на консоль в следующем примере:

unsigned __stdcall thread_proc(void*)

{

// Начало второй транзакции

CProxy& prx = CObject::GetObject(CObject::READ_UNCOMMITTED);

// Здесь поток будет ожидать примерно 1 сек. До тех пор, пока

// в главном потоке не будет выполнена строчка prx.Commit();

prx.value = 20;

prx.Commit();

return 0;

}

int main(int argc, char* argv[])

{

//Начало транзакции с 0 уровнем изоляции

CProxy& prx = CObject::GetObject(CObject::READ_UNCOMMITTED);

//Изменение данных

prx.value = 10;

//Открываем новую сессию

_beginthreadex(0,0,thread_proc,0,0,0);

//Print CObject::value variable

printf("%d\n",prx.value);

prx.Commit();

return 0;

}

На экран будет выведено число 10, а второй поток изменит данные только после фиксирования транзакции в главном потоке.

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Нашёл ошибку?
Или хочешь предложить что-то улучшить на этой странице? Напиши об этом и получи бонус!
Бонус рассчитывается индивидуально в каждом случае и может быть в виде баллов или бесплатной услуги от студизбы.
Предложить исправление
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5076
Авторов
на СтудИзбе
455
Средний доход
с одного платного файла
Обучение Подробнее