Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
4.2. БЛОКИРОВКИ 331
блокировку при доступе к какому-либо ресурс, нужно определить, при­сутствует ли признак одной из ситуаций необходимости блокирования. По своему назначению блокировки могут использоваться для случаев:
1. Доступ к ресурсу, который по своему физическому устройству мо­жет быть использован строго монопольно только одним процессом. Например, процессы могут определить, что COM порт занят имен­но в прикладной программе по соглашению об имени соответству­ющей блокировки, не обращаясь к команде открытия порта.
2. Изменение данных таким образом, что новое состояние зависит от предыдущего состояния этой же записи или от состояния других записей.
Наиболее часто блокировки используются для второй задачи. Пред­положим, что процессу необходимо получить следующее значение иден­тификатора. Для этого берется предыдущее, увеличивается на единицу и записывается новое. Полученное значение используется далее в про­грамме. Например:
NextId()
n id l +^DATA s id=$g(^DATA)+1 s ^DATA=id l -^DATA q id
Здесь программа меняет значение записи в глобале на основе преды­дущего значения этой же записи. Если не выполнять блокирование, то между операцией получения предыдущего значения и записью нового вычисленного может произойти такая же операция в другом процессе, и действия одного из этих процессов будут утрачены и процессы будут оперировать не взаимосогласованными значениями.
Другим примером является построение индексной записи. В этом случае значение записи в индексном глобале выполняется на основе за­писи в глобале, содержащем индексируемые данные. Если не выполнять блокирование, то другой процесс может получить из индексных записей информацию, не соответствующую реально существующим данным.
По умолчанию команда блокирования ожидает возможности блоки­ровки бесконечное время, хотя корректнее говорить о неограниченном времени. Во многих практических задачах обычно можно определить некое разумное время ожидания блокировки, по истечении которого
332 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
необходимо принять решение о продолжении выполнения программы и о передаче управления на другую команду.
Время ожидания блокировки указывается после имени блокировки:
lock name:timeout
где время ожидания указывается в секундах.
В зависимости от реализации или версии М системы может распозна­ваться дробное значение таймаута. Например, в MiniM поддерживается указание с точностью до миллисекунд. При использовании конкретной М системы нужно проверить, если это необходимо, поддерживается ли дробное число в качестве таймаута.
Одна из основных задач таймаута - определить либо то, что, возмож­но, было возникновение дедлока и дальнейшее выполнение программы невозможно, либо что другой процесс не выполнил отпускание блоки­ровки и сделал блокирование невозможным.
Если указан таймаут ожидания блокировки, то М система приоста­навливает выполнение процесса либо до получения блокировки либо до истечения таймаута. В случае если блокирование оказалось неуспеш­ным, то для процесса взводится значение системной переменной $TEST в значение 0. Если успешно, то в значение 1.
Одно из простых правил разработки на М состоит в том, что если был указан таймаут ожидания для какой-либо операции, то следующей вы­полняемой командой обычно должна стоять команда проверки значения $TEST, например командой IF или командой ELSE.
Все установленные процессом блокировки принадлежат этому про­цессу. В случае если процесс завершился каким-либо образом, аварийно или самостоятельно командой HALT, и не снял установленные им бло­кировки, то М система автоматически снимает все установленные им блокировки.
Механизм снятия блокировок в этом случае работает в зависимости от примененной MUMPS системы, в разных системах он может быть выполнен по-разному и может содержать некоторый период, в течении которого блокировка может существовать, но принадлежать несуществу­ющему процессу. В определенной степени верно то, что при аварийном завершении процесса данные, принадлежащие ему, но находящиеся в общей области памяти сервера, в любом случае какое-то время про­должают там находиться и не удаляются в тот же самый физический момент времени. Традиционно MUMPS системы стремятся сократить этот период очистки до физически предельного минимума с тем, что­бы при запуске следующего процесса, в случае если его номер совпадет
4.3. ТРАНЗАКЦИИ 333
с закончившимся, блокировки не были переданы новому процессу как нежданное наследство.

4.3 Транзакции

Для MUMPS систем, как для СУБД, одним из весьма практичных ме­ханизмов является механизм транзакций. Первоначально, когда язык MUMPS был только стандартизирован, механизм транзакций и его по­ведение не были определены. При этом для MUMPS были разработаны огромное количество программ, выполняющих критически важные зада­чи. И, для совместимости с предыдущими реализациями по поведению, те М системы, которые поддерживают транзакции, поддерживают их так, что если процесс не выполнил команды начала транзакции, то по умолчанию он работает вне транзакционных скобок.
M системы поддерживают, как минимум, команды начала транзак­ции, подтверждения транзакции и отката транзакции. В зависимости от внутренней архитектуры, используются ли оптимистические или песси­мистические блокировки данных, система может поддерживать также команду повтора транзакции. Если М система не поддерживает тран­закции, либо какую-либо из команд, то при попытке выполнить такую команду должна генерироваться ошибка о том, что эта команда не под­держивается.
Например, системы MiniM и Cach´e реализуют команды tstart, tcommit и trollback и парную им системную переменную $tlevel, но на команду trestart генерируют ошибку о том, что команда не поддерживается. Си­стема GT.M в силу внутренней архитектуры дополнительно поддержи­вает команду trestart и парную ей системную переменную $TRESTART. Более старые системы MSM могут не генерировать ошибку при выпол­нении транзакционных команд, но при этом команды могут не выпол­няться, поскольку система так и осталась недоработанной в отношении транзакций. Система M3-Lite при использовании транзакционных ко­манд также ничего не делает и системная переменная $tlevel всегда возвращает значение 0. Таким образом, при необходимости использо­вать транзакции разработчики должны проверить характер и, возможно, особенности поведения транзакций на используемой М системе.
Для М системы транзакция - это с одной стороны состояние про­цесса, с другой стороны совокупность сделанных от начала транзакции изменений в глобалах.
По умолчанию при старте процесса его уровень транзакции 0, что означает что сделанные в этом состоянии изменения не откатываются.
334 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
При каждом вызове команды tstart уровень транзакции увеличивается на единицу. Начиная со значения 1 изменения в глобалах могут быть возвращены в состояние на начало транзакции, когда значение уровня транзакции было 0. Произвольные библиотечные функции и подпрограм­мы, таким образом, могут просто использовать транзакционные скобки tstart - tcommit по необходимости, не влияя на контекст транзакции вызвавшего их кода.
При каждом вызове команды tcommit система уменьшает на едини­цу уровень транзакции. При снижении до 0 процесс переходит снова в состояние вне транзакции.
При вызове команды trollback М система откатывает все изменения глобалов сделанные процессом от входа в состояние транзакции 1, в предыдущее состояние и переводят уровень транзакции в состояние 0. Например, если есть код
s ^a(0)=0 tstart ; $tlevel = 1 s ^a(1)=1
tstart ; $tlevel=2 s ^a(2)=2 tcommit ; $tlevel=1
tstart ; $tlevel = 2 s ^a(3)=3 trollback ; $tlevel = 0
то команда rollback вернет предыдущее состояние и для ˆa(3) и для ˆa(2) и для ˆa(1), поскольку все они были изменены в состоянии $tlevel>0, но не вернет предыдущее состояние для ˆa(0).
Пока выполняется изменение глобалов в контексте транзакции, все эти изменения могут быть видны другим процессам или не видны. Си­стемы MiniM и Cach´e работают в режиме видимости всех сделанных из­менений, то есть в пессимистической блокировке. Система GT.M может поддерживать оптимистическую блокировку и другие процессы могут не видеть изменений, сделанных в контексте транзакций.
Первый из этих случаев ориентирован на большую работу при отка­те сделанных изменений (trollback) и имеет легковесное подтверждение (tcommit), второй ориентирован на большую работу при подтвержде­нии сделанных изменений (tcommit) и легковесный откат (trollback) и, в принципе, может потребовать очень больших ресурсов как оперативной памяти, так и дисковой.
При использовании второго случая разработчики должны соразме­рять операции, которые предстоит выполнить их прикладной системе, с
4.3. ТРАНЗАКЦИИ 335
аппаратными возможностями сервера, на котором программа будет вы­полняться. Традиционным решением проблемы в таких случаях явля­ется разбиение выполняемых действий, одной длинной транзакции, на несколько коротких транзакций. С другой стороны, это, теоретически, уже не транзакция, и нет необходимости входить в такой режим. Обычно проблема длинных транзакций возникает в тех СУБД, где нет возмож­ности отключить контекст транзакции или переключиться на пессими­стическую блокировку.
В техническом отношении режим оптимистической блокировки вы­полняется ведением версий данных, а режим пессимистической блоки­ровки выполняется ведением отката по журналу выполненных действий. Таким образом, для корректной работы транзакций во втором случае (как в MiniM и в Cach´e) необходимо, чтобы работало журналирование для используемой базы данных.
Интересной особенностью систем MiniM и Cach´e является возмож­ность на время отключить и снова включить журналирование для про­цесса. При его выключении не для базы данных, а для процесса, сделан­ные им изменения попадают в базу данных, но не попадают в журнал. А, поскольку откат транзакции работает по журналу, все изменения, сде­ланные при временном выключении журналирования, не откатываются.
Другой интересной особенностью, поддерживаемой Cach´e, но не под­держиваемой MiniM, является возможность административно явно ука­зать, для каких именно глобалов в одной и той же базе данных исполь­зовать или не использовать журналирование.
При изменении глобалов процесс может выполнить всего 4 различных с точки зрения транзакционного механизма действия:
1. Создать запись переменной, которая не существовала.
2. Перезаписать существовавшую переменную.
3. Удалить существовавшую переменную.
4. Удалить не существовавшую переменную.
В первом случае откат транзакции возвращает переменную в состо­яние неопределенного значения, но только для именно этого имени, не удаляя вложенных имен. Во втором случае откат возвращает предыду­щее значение. В третьем случае откат возвращает существование пере­менной и ее предыдущее значение. Если было удаление с вложенными именами, то они также возвращаются в свое значение.
336 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
Четвертый пункт ни в MiniM, ни в Cach´e, ничего не делает. Глобалы являются деревьями и операция удаления это удаление также всех вло­женных переменных. Но в журнале среди записей, выполненных процес­сом, не содержится, с какими из них выполнялись операции, поскольку при удалении несуществовавшей переменной удалять было нечего. Ес­ли в течении транзакции после удаления несуществовавшей переменной другой процесс запишет то-то в этот глобал, то эти записи останутся и глобал не будет переведен в состояние несуществующего.
В отношении четвертого пункта у различных СУБД может быть рас­хождение во мнениях, то необходимо предпринять - ничего не делать или восстановить значение в неопределенное. В случае MUMPS систем переменные являются деревьями и восстановление в неопределенное зна­чение лишь одного корневого имени без его дочерних имен выглядело бы неестественным и не цельным действием. Удаление же тех узлов, кото­рые процесс не изменял, означает изменение тех узлов, которые не были затронуты, что противоречит принципу отката транзакции откатывать именно выполненные процессом изменения.
Для MUMPS систем, поддерживающих транзакции, поддерживается поведение по умолчанию для завершения процесса, если он завершился в контексте транзакции. В зависимости от версии и реализации это мо­гут быть либо откат сделанных изменений, либо игнорирование отката, что в силу пропадания процесса эквивалентно подтверждению сделан­ных изменений. Также поведение MUMPS системы может зависеть от архитектуры, используется ли оптимистическая или пессимистическая блокировка.

4.4 Блокировки в транзакциях

MUMPS системы, реализующие транзакции, имеют особенность пове­дения блокировок, выполненных в транзакциях. Эти два механизма вы­полнены взаимосвязанно, хотя, казалось бы, на первый взгляд это раз­личные механизмы.
Если выполняется блокирование глобалов, то для СУБД это означает, что производится важная для взаимосогласованности данных операция. Если эта операция выполняется в контекте транзакции, то выполнен­ные изменения могут быть впоследствии либо отменены целиком, либо подтверждены целиком.
Оба эти механизма, и транзакции, и блокировки, ориентированы на корректное выполнение конкурентного доступа к данным. Поэтому, для того, чтобы дать другим процессам доступ либо к подтвержденным, либо
4.4. БЛОКИРОВКИ В ТРАНЗАКЦИЯХ 337
к отмененным данным, транзакционные MUMPS системы удерживают блокировки до окончания транзакции независимо от команды удаления блокировки, если блокировка была выполнена в транзакционном контек­сте.
Удержание блокировки выражается в том, что при выполнении коман­ды снятия блокировки в действительности эта блокировка не снимается физически, а лишь маркируется для снятия по окончании транзакции.
Различные реализации MUMPS могут по-разному выполнять нюансы этого механизма, поскольку стандартом не описано точное поведение си­стемы при удержании блокировки до окончания транзакции. Например, некоторый список отличий:
1. Система MiniM удерживает блокировку до конца транзакции, если она была установлена при ненулевом $tlevel. Система Cach´e удер­живает любые блокировки, пока текущий $tlevel не нулевой, в том числе установленные при нулевом $tlevel.
2. Система MiniM при команде снятия всех блокировок (безаргумент­ный lock) физически снимает блокировки независимо от контек­ста транзакции. Система Cach´e продолжает удерживать блокиров­ки при безаргументном lock.
Кроме того, различные MUMPS системы могут давать специфичный от реализации механизм принудительного физического удаления блоки­ровок, в том числе установленные другим процессом.
В любом случае, различные реализации MUMPS систем ориентиро­ваны на типовое использование блокировок в транзакциях в инкремент­ной форме. При написании типового кода разработчики на М получают наиболее ожидаемое поведение СУБД и конкурентного доступа для раз­личных процессов.
Механизм взаимозависимости блокировок имеет очень важное след­ствие в том, что касается массовой обработки данных в транзакции. Для пояснения нужно сделать небольшой отступ к упрощенной классифика­ции СУБД с нечеткими границами между различными типами СУБД:
1. Малые базы. Данных так мало, что для данных не требуются ни специальные форматы, ни специальные алгоритмы. Например, для хранения данных могут использоваться плохоструктурированные с точки зрения СУБД форматы - INI, XML.
2. Средние базы. Данных уже много, для них применяются специ­альные форматы, характерные для СУБД, и зачастую индексные структуры и алгоритмы. Например, форматы DBF.
338 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
3. Большие базы. Данных столь много, что к специальным алгорит­мам добавляются специальные методы кеширования и подкачки, поскольку объем обрабатываемых за одну операцию данных пре­вышает размер оперативной памяти.
4. Очень большие базы (VLDB). К функциям СУБД добавляется ке­ширование и подкачка также и служебных данных, поскольку и их объем также не умещается в оперативной памяти.
Здесь важным пунктом является четвертый. Блокировки, по сути, яв­ляются служебными данными, существующими временно. В СУБД клас­са VLDB служебные данные изначально предполагаются в таких объе­мах, что их сами необходимо хранить на диске и кешировать. Например, служебные данные о блокировках могут быть записаны в служебные по­ля базы данных или в отдельном файле. В определенной степени сужде­ние о том, то задача относится к классу VLDB, определяется соотноше­нием объемов обрабатываемых данных и аппаратными возможностями. Первые реализации VLDB систем обрабатывали такие объемы данных, с которыми современные 64-битные компьютеры могут справляться со­вершенно непринужденно, имея объем оперативной памяти больше, чем объем дисковой для тех первых систем.
Разработчики должны понимать, что в настоящее время MUMPS си­стемы не относятся к СУБД класса VLDB, поскольку блокировки хранят строго в оперативной памяти, в пределах указанных администратором в настройках сервера.
Важным следствием является то, что разработчикам для выполнения в транзакции массового изменения данных необходимо учитывать об­щее ограничение на объем блокировок. Это является общей проблемой не MUMPS систем, а различных СУБД в целом, вне зависимости от архитектуры.
Традиционно разработчики выбирают один из вариантов:
1. Выполнение массовых операций в специальном внетранзакционном контексте.
2. Выполнение массовых операций без блокировок.
3. Разбиение массовой операции на меньшие порции.
Еще одним внесистемным решением проблемы большого числа бло­кировок при выполнении массовых операций является метод эскалации блокировок. Существуют также такие реализации СУБД, которые под­держивают этот метод автоматически. Суть метода эскалации состоит в
4.4. БЛОКИРОВКИ В ТРАНЗАКЦИЯХ 339
том, чтобы, немного нарушив принцип презумпции блокирования, заме­нить большое число детальных блокировок на меньшее число логически включающих их укрупненных блокировок.
Предположим, что для выполнения действия необходимо блокирова-
ние большого числа однородных узлов
^DATA("abc",123) ^DATA("abc",456) ^DATA("abc",789) ^DATA("abc",...) ^DATA("def",123) ^DATA("def",456) ^DATA("def",789) ^DATA("def",...) ^DATA("tyu",123) ^DATA("tyu",456) ^DATA("tyu",789) ^DATA("tyu",...) ...
Этот список мы можем заменить на меньшее число блокировок более
высокого уровня
^DATA("abc") ^DATA("def") ^DATA("tyu") ...
Либо на совсем радикальную общую блокировку
^DATA
Соответственно, в методе эскалации различают решение по степе­ни детализации эскалирования, по возможности попадания процессов в дедлок и по возможности выполнить эскалацию в автоматическом ре­жиме. В разработках на MUMPS обычно разработчики не применяют метод автоматической эскалации из-за трудоемкости ее выполнения и существенной зависимости снятия блокировок в контексте транзакции от реализации MUMPS системы, а реализуют работу массовой опера­ции в контексте изначально укрупненных блокировок.
При необходимости обеспечить целостность всей базы до и после массовой операции разработчики могут использовать замену транзакций на бекап. При выполнении бекапа до массового перестроения данных у администратора существует возможность вернуть базу данных в преды­дущее корректное состояние в случае сбоя.
340 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП

4.5 Функция $INCREMENT

Транзакции с их возможностью выполнить отмену произведенных дей­ствий это совсем не безобидный механизм, просто сам по себе что-то улучшающий, и его нельзя просто добавить к коду, разработанному для контекста без транзакций.
Рассмотрим для примера операцию добавления записи. При добавле­нии записи необходимо получить ее очередной номер, или суррогатный ключ:
NextId()
n id l +^DATA s id=$g(^DATA)+1 s ^DATA=id l -^DATA q id
Этот код вполне корректен для работы вне транзакций. Пока мы опе­рируем арифметикой, вычисляем значение следующего идентификатора, записываем, код блокирует глобал и несколько процессов получают каж­дый для себя следующий номер.
Но этот код некорректен с точки зрения работы в транзакции. Если первый процесс получит значение 1, второй процесс значение 2, а тре­тий процесс значение 3, то при выполнении вторым процессом отката транзакции произойдет возвращение значения
^DATA
к значению, бывшему перед получением значения 2, то есть откат тран­закции установит этот счетчик в значение 1.
Следующий, четвертый процесс, получит соответственно номер 2, а пятый номер 3, что очевидно не соответствует корректной работе функ­ции получения следующего номера суррогатного ключа.
Для корректной работы конкурентного доступа процессов в контексте транзакции СУБД традиционно предусматривают механизм, парный к механизму отката транзакции, или операции, действие которых откатом транзакции не возвращается назад.
В MUMPS системах наиболее часто используемой операцией, для ко­торой требуется невозврат значения в предыдущее состояние, является операция получения следущего идентификатора. В других СУБД так­же предусматриваются различные аналогичные механизмы, например в Oracle это sequence, в INTERBASE это generators.