Решение трех проблем с помощью механизма блокировок в СУБД SQLAnywhere
1*) Решение первой проблемы (утраченное обновление)
Время | ТА | Значения | ТВ | Комментарий |
t1 | start | |||
t2 | Сч1=Select R | Сч1 (ТА)=50 | С-запрос на R от ТА и захват | |
t3 | start | |||
t4 | Сч1 (ТВ)=50 | Сч1=Select R | С-запрос на R от ТВ и захват | |
t5 | UPDATE R=Сч1+15 | М-запрос на R от ТА и отказ | ||
t6 | ждать | ТА ждет завершения ТВ | ||
t7 | UPDATE R=Сч1+15 | М-запрос на R от ТВ и отказ | ||
t8 | ждать | ТВ ждет завершения ТА |
Захваты и тупики
С помощью захватов решается проблема утраченного обновления, но появляется проблема тупика - взаимной блокировки нескольких транзакций, при которой каждая из транзакций ждет, пока другая снимет захват.
Если возникает тупиковая ситуация, то ее обнаруживает и разрушает СУБД.
Чтобы ликвидировать тупик, СУБД выбирает из вовлеченных в тупик транзакций одну - жертву - и принудительно выполняет ее откат.
2*) Решение проблемы зависимости от неподтвержденных обновлений с помощью механизма блокировки
Время | ТА | Значения | ТВ | Комментарий |
t0 | R=50 | start | ||
t1 | R=20 | UPDATE R=20 | М-запрос на R от ТВ и захват | |
t2 | start | |||
t3 | Сч1=SELECT R | С-запрос на R от ТА и отказ | ||
t4 | ждать | ТА ждет завершения ТВ (блокир) | ||
t5 | ждать | R=50 | ROLLBACK | ТВ заканчивается откатом, ТА разблокируется |
t6 | Сч1=SELECT R | Сч1 (ТА)=50, R=50 | С-запрос на R от ТА и захват |
3*) Решение проблемы противоречивости с помощью механизма блокировки
Время | ТА | Значения | ТВ | Комментарий |
t0 | start | R1=40, R2=50, R3=30 | ||
t1 | Сч1=SELECT R1 | Сч1 (ТА)=40, Сум (ТА)=40 | С-запрос на R1 от TA и захват | |
t2 | Сч2=SELECT R2 | Сч2 (ТА)=50, Сум (ТА)=90 | С-запрос на R2 от TA и захват | |
t3 | start | |||
t4 | Сч3 (ТВ)=30 | Сч3=SELECT R3 | С-запрос на R3 от TВ и захват | |
t5 | R3=20 | UPDATE R3=Сч3-10 | М-запрос на R3 от TB и захват | |
t6 | Сч1 (ТВ)=40 | Сч1=SELECT R1 | С-запрос на R1 от ТВ и захват | |
t7 | UPDATE R1=Сч1+10 | М-запрос на R1 от ТВ и отказ, т.к. есть С-запрос на R1 от ТА | ||
t8 | ждать | ТВ ждет завершения ТА | ||
t9 | Сч3=SELECT R3 | ждать | С-запрос на R3 от ТА и отказ, т.к. есть М-запрос на R3 от ТВ | |
t10 | ждать | ждать | ТА ждет завершения ТВ | |
Тупик, который приведет к принудительному откату одной из транзакций |
Главная идея этого подхода не в том, чтобы изолировать объект в БД от другой транзакции, а в том, чтобы создать отдельную копию объекта для каждой параллельной транзакции. Тогда транзакции от своего начала и до конца будут работать с собственными копиями объекта. По завершении транзакции все сделанные ей изменения могут быть перенесены из ее копии объекта в БД.
Некоторые обстоятельства:
1) Очевидно, что беспрепятственно изменить свою копию сможет лишь одна из параллельных транзакций, которая сделает это первой (TA). СУБД не позволит другой транзакции (TB) изменить тот же объект и, либо аварийно завершит TB, либо переведет TB в ожидание до конца TA, что зависит от заданных для TB условий старта.
Если TB будет ждать завершения TA, то возможны два исхода:
а) TA подтверждает свои изменения (COMMIT), тогда TB аварийно завершается;
б) TA отменяет свои изменения (ROLLBACK), тогда TB переносит свои изменения в БД и продолжает работать.
2) Каждая параллельная транзакция может беспрепятственно читать и всегда видеть в точности то состояние БД, которое было когда транзакция началась, плюс ее собственные изменения, но не может видеть изменения в БД, сделанные другими параллельными транзакциями.
Решение трех проблем с помощью многоверсионных объектов в СУБД Interbase
Объектом для транзакции является строка.
Interbase создает новую версию строки всякий раз, когда какая-либо транзакция изменяет или удаляет эту строку. Interbase помечает эту версию строки временем появления и именем транзакции, которая воздействовала на строку.
По запросу любой транзакции на чтение этой строки, Interbase предоставляет ей самую свежую версию строки, но уже подтвержденную до старта этой транзакции.
1**) Решение первой проблемы (утраченное обновление)
Время | ТА | Значения | ТВ | Комментарий |
t0 | R (t0, TX)=50 | Последняя подтвержденная версия R на t0 | ||
t1 | start | |||
t2 | Сч1=SELECT R | Сч1 (ТА)=50 | ТА получает подтвержденную версию от времени t0<t1 | |
t3 | start | |||
t4 | Сч1 (ТВ)=50 | Сч1=SELECT R | ТВ получает подтвержденную версию от времени t0<t3 | |
t5 | UPDATE R=Сч1+15 | R (t5, ТА) = 65 ? | Не известно, будет ли подтверждено изменение | |
t6 | Сч1 (ТВ)=50 | Сч1=SELECT R | Версия от t5 для TB не доступна, так как t5>t3 | |
t7 | UPDATE R=Сч1-10 | TB блокируется, т.к. есть версия R от t5 и t5>t3 | ||
t8 | ждать | ТВ ждет завершения ТА | ||
t9 | COMMIT | R (t9,TA)=65 | конфликт | Подтвержденная версия от t9 для TB не доступна, т.к. t9>t3 |
t10 | ROLLBACK | TB должна отменить свои изменения | ||
t11 | start (ТВ') | |||
t12 | Сч1 (ТВ')=65 | Сч1=SELECT R | t12>t9 | |
t13 | R (t13, TB')=55 ? | UPDATE R=Сч1-10 | ||
t14 | R (t14, TB')=55 | COMMIT |
TA беспрепятственно изменило R, поскольку сделало это первой. TB не удалось изменить R, так как она не "видела" самой последней подтвержденной версии.
(1**) решает проблему (1) и, в отличие от (1*), исключает тупиковую ситуацию.
2**) Решение проблемы зависимости от неподтвержденных обновлений с помощью многоверсионных объектов
Время | ТА | Значения | ТВ | Комментарий |
t0 | R=50 | start | Последняя подтвержденная версия R на t0 | |
t1 | R(t1,TB)=20 ? | UPDATER=20 | Не известно, будет ли подтверждено изменение | |
t2 | start | |||
t3 | Сч1=SELECT R | R(t3,TA)=50, Сч1(ТА)=50 | Последняя подтвержденная версия R на t3 | |
t4 | R=50, R(t4,TA)=50, Сч1(ТА)=50 | ROLLBACK | ТВ отменяет свои изменения и заканчивается, т.е. изменения ТВ в БД не переносятся | |
t5 | ТА может спокойно продолать работу |
3**) Решение проблемы противоречивости с помощью многоверсионных объектов
Время | ТА | Значения | ТВ | Комментарий |
t0 | start | R1=40, R2=50, R3=30 | Последние подтвержденные версии R1, R2, R3 | |
t1 | Сч1=SELECT R1 | R1(t1,TA)=40, Сч1(ТА)=40, Сум(ТА)=40 | ||
t2 | Сч2=SELECT R2 | R1(t2,TA)=50, Сч2(ТА)=50, Сум(ТА)=90 | ||
t3 | R1(TB)=40, R2(TB)=50, R3(TB)=30 | start | ||
t4 | Сч3(ТВ)=30 | Сч3=SELECT R3 | ||
t5 | R3(TB)=20 ? | UPDATE R3=Сч3-10 | Не известно, будет ли подтверждено изменение | |
t6 | Сч1(ТВ)=40 | Сч1=SELECT R1 | ||
t7 | R1(TB)=50 ? | UPDATE R1=Сч1+10 | Не известно, будет ли подтверждено изменение | |
t8 | R1(TB)=50, R3(TB)=20 R1=50, R2=50, R3=20 | COMMIT | Конфликт: подтвержденные версии R1 и R3 для ТА не доступны, т.к. t8>t0 | |
t9 | ROLLBACK | ТА должна сделать откат | ||
t10 | start (TA') | R1(TA')=50, R2(TA')=50, R3(TA')=20 | Последние подтвержденные версии R1, R2, R3 | |
t11 | Сч1=SELECT R1 | Сч1(ТА')=50, Сум(ТА')=50 | ||
t12 | Сч2=SELECT R2 | Сч2(ТА')=50, Сум(ТА')=100 | 50+50=100 | |
t13 | Сч3=SELECT R3 | Сч3(ТА')=20, Сум(ТА')=120 | 100+20=120 Получили правильный результат для ТА: 120 |
Тема 13. Объектно-ориентированное программирование
Существует две модели построения программ:
- процессно-ориентированная;
- объектно-ориентированная.
Процессно-ориентированная. В данной модели программа представляется как ряд последовательно выполняемых операций (процедур). Языки программирования, в которых реализован процессно-ориентированный подход к построению программ, называются процедурными.
Объектно-ориентированная. При использовании данной модели программа рассматривается как совокупность объектов – отдельных фрагментов кода, обеспечивающих выполнение определенных действий и объединяющих данные и методы управления ими.
13.1. Основы языка ObjectPascal
Программа, написанная на языке ObjectPascal, состоит из ряда разделов. Начало каждого раздела указывается с помощью специальных зарезервированных слов. В общем виде программа ObjectPascal имеет следующий вид:
Program Name_of_programUses Unit1, Unit2, Unit3Label Label1, Label2Type id_type1 = type_def1; id_type2 = type_def2;Var id_var1: type_def1; id_var2, id_var3: id_type2;Const id_const1 = value1; id_const2 = value2; id_const3 = expression1;Procedure proc1;текстпроцедурыFunction Func1: type_def1;текст функцииbegin текст программыend. | Заголовок программыРаздел объявления используемых модулейРаздел объявления используемых метокРаздел описания типов, определяемых пользователемРаздел объявления переменныхРаздел описания константРаздел объявления процедур и функций, используемых в программе |
Заголовок программы. В заголовке после служебного слова Program указывается имя программы.
Раздел объявления модулей. Начало данного раздела указывается с помощью директивы Uses.