Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
7.8. ВОЗВРАТ РЕЗУЛЬТАТОВ 491
q
Indir(name)
s @name@("ret")="Calculate "_@name@("data") q
Здесь вычисляющая функция записывает результат, используя кос­венность имени переменной. Кроме того, заодно в этой же переменной ей передается исходное значение:
USER>k d RunIndir^RETURN w
var("data")=123 var("ret")="Calculate 123" USER>
Такой сособ возврата значений, также как и передачу по ссылке, можно свободно комбинировать с возвратом по значению.
К подводным камням такого способа можно отнести случайную воз­можность передать в вычисляющую функцию имя локальной перемен­ной, которое уже используется ей внутри. В этом случае произойдет применение операции косвенности к внутренней переменной:
RunIndir ; k d RunIndir^RETURN w
n var s var("data")="123" d Indir($na(var)) w q
Indir(name)
n var s var=456 s @name@("ret")="Calculate "_$g(@name@("data")) s @name@("ret")="Calculate "_var q
И вызываемая функция не получит результат:
USER>k d RunIndir^RETURN w
var("data")=123 USER>
Такая ситуация может случайно произойти при использовании часто употребимых имен временных локальных переменных.
Возврат значений косвенно обычно применяется при необходимости вернуть большой объем данных, и зачастую для такого способа исполь­зуются не локальные, а глобальные переменные. Объем возврата, в прин­ципе, может быть любым, в пределах, доступных базе данных. Разуме­ется, при передаче имени глобала коллизий по именам с локальными переменными произойти не может.
492 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ

7.8.5 Итеративный возврат

Под итеративным возвратом понимается возврат из вычисляющей функ­ции очередной порции результата. Для получения общего результата в этом случае нужно вызвать вычисляющую функцию несколько раз, обычно в цикле.
Для организации итеративного возврата ключевым моментом являет­ся соглашение о передаче состояния очередной итерации таким образом, чтобы обе стороны могли определить, является ли вызов начальным, завершающим, или необходимо продолжить итерацию далее.
В простых случаях таким индикатором является само значение итера­тора, которое создается и проверяется вызывающей стороной, например, на равенство пустой строке или иному предопределенному значению.
Приведем простой пример итеративного возврата, где критерием как начала так и окончания итерации является равенство пустой строке са­мого итератора:
RunIter ; k d RunIter^RETURN w
n iter,value s iter="" f s value=$$Iter(.iter) q:iter="" d . w "Next: ",value,! q
Iter(index)
s index=$o(^rMAC(index)) i index="" q "" q index_": "_$g(^rMAC(index,0))
При выполнении такого кода возвращается перечень имеющихся мак­рорутин с датами их модификации:
USER>k d RunIter^RETURN w Next: BREAK: 62682,53568 Next: DTC: 62623,48148 Next: ETRAP: 62671,63182 Next: ETSTACK: 62659,70354 Next: EXTRUN: 62686,58570 Next: RETURN: 62690,54306 Next: STACK: 62682,82491 Next: ZTRAP: 62651,61619
В более сложных случаях может быть использовано усложнение со­глашений: использование структурного итератора, специальной функции создания начального значения итератора и, возможно, специальных вре­менных служебных данных, использование специальной функции про­верки критерия окончания итерации, использование специальной функ­ции завершения итерации с удалением временных данных.
7.8. ВОЗВРАТ РЕЗУЛЬТАТОВ 493
В определенном смысле такое полное построение итеративного воз-
врата аналогично запросу с соответствующим набором функций:
RunIter ; k d RunIter^RETURN w
n iter,value d ICreate(.iter) f d INext(.iter) q:$$IEOF(.iter) d . s value=$$IValue(.iter) . w value,! d IClose(.iter) q
ICreate(index)
s index="" q
INext(index)
s index=$o(^rMAC(index)) q
IValue(index)
q index_": "_$g(^rMAC(index,0))
IEOF(index)
q index=""
IClose(index)
q
С переходом к такому обобщенному итератору его набор функций мо­жет быть изменен в любое время с использованием новых требований, предъявляемых к запросу, но все вызывающие функции останутся неиз­менными, поскольку не используют явных критериев начала и окончания итераций.
Итеративный возврат применяется обычно в ситуациях, когда объ­ем возвращаемых данных непредсказуем, но передавать результат через предварительную запись в промежутоный временный глобал с последу­ющим его анализом по каким-либо причинам нецелесообразно.

7.8.6 Потоковый возврат

Потоковый возврат применяется для возврата результата в устройство с помощью команд записи в устройство.
Такие виды возврата вычисленных значений применяются при генера­ции ответа клиентским программам, например при обслуживании TCP
- соединения клиентской программы с сервером, при генерации WEB страницы, при генерации файла.
Результат вычислений в этом случае возвращается не вызывающей функции, а в устройство ввода-вывода.
494 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
При использовании потокового возврата, конечно, вызывающая функ­ция должна придерживаться одинаковых соглашений с вызываемой функ­цией об устройстве ввода-вывода для получения результата: использует­ся ли текущее устройство, или следует передать имя устройства, которое будет открыто и использовано на запись.

7.9 %Z - рутины

При эксплуатации MUMPS систем и при разработке программного обес­печения для них разработчикам может понадобиться размещать часть своих рутин так, чтобы они были доступны на выполнение из любой ба­зы данных. Практически все реализации MUMPS систем поддерживают правило отображения рутин из некоей особой базы данных на остальные так, что рутины можно вызывать, не указывая явно базы данных где они хранятся, как если бы они находились в текущей базе данных.
Практически все современные СУБД, поддерживающие хранение и исполнение подпрограмм в базе данных, в той или иной мере поддер­живают такую функциональность. Такую базу данных обычно называ­ют системной или аналогиным пользуются термином (в зависимости от предпочтений производителя), и в ней размещают рутины, относящиеся ко всем базам данных, а не только к определенной прикладной програм­ме. Либо относящиеся к прикладной программе, которая используется различными процессами, работающими в различных базах данных.
При этом возникает вопрос коллизий имен рутин между различны­ми разработчиками. При эксплуатации СУБД при выполнении апгрейда на последующую версию инсталлятор устанавливает в системную базу данных комплект рутин, входящий в эту версию. Для того, чтобы не возникло коллизий с именами рутин и с замещением рутин с таким же именем, не принадлежащих самой СУБД, производители рекомендуют использовать специальные соглашения об именовании рутин, размещяе­мых в системной базе данных.
Рутины, которые должны отображаться на другие базы данных, долж­ны начинаться на символ процент (%). При этом производители MUMPS систем не используют имена рутин, начинающиеся на символы %Z, в стандартной комплектации.
Группа имен рутин, начинающихя на символы %Z, таким образом, отводится для разработчиков прикладных программ или для дополни­тельных модулей третьих лиц.
Исторически сложилось так, что практически все системные про­граммы производители MUMPS систем именуют в верхнем регистре. В
7.9. %Z - РУТИНЫ 495
каких-то случаях это делается из соображений совместимости, в каких­то из сохранения общего стиля.
И, как следствие этого неформализованного правила, независимые разработчики также могут использовать процентные рутины с имена­ми, содержащими символы в нижнем регистре, не опасаясь коллизий со стороны процесса апгрейда.
Кроме проблемы коллизий с набором имен, используемым применя­емой СУБД, также стоит вопрос коллизий между различными произво­дителями прикладных программ общего назначения и различных биб­лиотек. Для этого случая также не существует официально формализо­ванного правила, но большинство разработчиков прибегает к механизму разделения имен таких рутин библиотечного назначения путем исполь­зования префиксов.
В качестве префикса обычно используются символы сокращения от названия библиотеки, например
%xdXXX %iaXXX %aXXX
где вместо символов XXX используется уже содержательное имя, как-бы уже имя рутины в самом этом пакете рутин.
Интересно то, что автору действительно довелось встретить случай, когда разработчики рутин общего библиотечного назначения не следова­ли правилам именования для избежания коллизий, и при переносе таких рутин на другую реализацию MUMPS системы действительно возникла коллизия по именам, причем устранить проблему оказалось непросто ­имя рутин использовалось также в нескольких приложениях на других языках. В этом случае было принято административное решение - после апгрейда снова устанавливать пакет библиотеки поверх имеющихся ру­тин и не использовать системную рутину, входящую в комплект СУБД. Проблема коллизии не возникала до тех пор, пока не произвели перенос проекта на другую СУБД. Такой случай показывает неверность утвер­ждения, что если прикладная система работает нормально, то в ней нет ошибок.
Общепринятой и сложившейся практикой среди разработчиков на MUMPS стало то, что те рутины, которые должны быть импортированы в одну базу данных, готовятся для импорта в виде одного комплекта фай­лов. Соответственно, для %Z рутин и библиотечных, устанавливаемых в системную базу данных, также готовится отдельный комплект файлов импорта и инструкция с указанием в какую базу данных их необходи­мо импортировать. Отдельные реализации MUMPS систем допускают
496 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
просто перенос подготовленного файла базы данных с необходимыми рутинами, но такой перенос для системной базы данных в общем случае не выполним.

7.10 Планирование файлов

СУБД оперирует данными, организованными в базы данных. При этом физическая организация баз данных может отличаться для СУБД раз­ных типов. Данные могут размещаться и храниться физически в памяти, поступать из внешних источников, храниться в виде файлов операцион­ной системы, использоваться сырые неразмеченные разделы дисков, хра­ниться на лентах. Применяемые в практике СУБД обычно комбинируют эти способы организации.
Пример организации виртуальной СУБД, не хранящей собственные данные, а получающей их из сторонних источников - WMI, когда при­ложение обращается с запросом, характерным скорее для СУБД, но с целью определить, например, температуру процессора или список рабо­тающих процессов.
Пример организации СУБД, работающей только с файлом - это пар­сер XML или INI файла, когда приложение обращается к данным, хра­нящимся в одном файле.
Промышленные СУБД обычно применяют хранение данных в виде файлов операционной системы, различные методы кеширования храни­мых данных и дополнительные служебные файлы для поддержания опе­раций транзакций, бекапа, процедуры восстановления после аппаратных и программных сбоев.
Один экземпляр промышленной СУБД оперирует обычно множеством файлов одновременно, выполняя при необходимости чтение и запись. И, для корректного планирования файлов данных, нужно понимать прин­ципы и способы организации работы дисковых накопителей.
Дисковый накопитель упрощенно представляет собой действительно диск или несколько, привод, перемещающуюся головку и систему управ­ления всем механизмом, заключенные в корпус. Ключевым элементом является головка чтения - записи. У каждого из шпинделей она одна, и, если программе необходимо выполнить чтение - запись, то головка долж­на выполнить физическое позиционирование к необходимому месту на диске. После выполнения операции чтения - записи головка должна спо­зиционироваться в другое место. Поскольку это процесс механический, то на каждое такое перепозиционирование уходит время. Если програм­ма оперирует одним сектором, то коэффициент использования головки
7.10. ПЛАНИРОВАНИЕ ФАЙЛОВ 497
высокий. Если несколькими, включая принадлежащие нескольким фай­лам, то низкий.
В идеале аппаратура должна предоставить по отдельной головке для каждого сектора, но такого не бывает, хотя промышленность уже нашла решение, но в виде выпуска твердотельных накопителей.
Для общего улучшения производительности (если имеется такая воз­можность) лучше разделять всю совокупность используемых файлов между как можно б´ольшим числом шпинделей. В идеале нужно по­местить каждый из используемых файлов на отдельный накопитель и базу данных разделить на несколько файлов на физически различных накопителях.
Физически это выполняется либо планированием размещения фай­лов на физически разных накопителях, либо применением различного рода RAID массивов, когда файлы автоматически аппаратно разделяют­ся между несколькими шпинделями.
Для планирования размещения файлов нужно определить, какие из файлов какими операциями используются - последовательными или про­изольными позиционированиями. В частности, если есть процесс записи журнала, то лучше ему предоставить под журнал отдельный диск, чтобы он не беспокоил головки дисков файлов данных.
Для составления списка файлов для размещения на физически раз­личных дисках нужно обратиться к описанию используемой СУБД, и определить способ переноса файлов и изменения конфигурации СУБД.
Исполняемые файлы (exe) после запуска процессов обычно более не используются, и образы процессов используются только в памяти. По­этому, если на сервере имеются более медленные и более быстрые диски, то лучше разместить исполняемые файлы СУБД на медленных, а файлы данных на быстрых дисках.
Как показывает практика применения различных дисковых конфи­гураций, даже распределение файлов СУБД между несколькими фи­зически различными одиночными дисками может поднять производи­тельность серверной системы в 2 - 4 раза. Применение RAID массивов соответствующих типов или твердотельных дисков также является пред­почтительным способом улучшения производительности.
В случае если на компьютере достаточно оперативной памяти, может быть организован также RAM - диск, и на него могут быть перенесены те файлы, которые могут быть потеряны без потери значимой информации (различного рода временные файлы).
498 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ

7.11 Память и сборка мусора

Одним из важных практических вопросов применения MUMPS систем является отношение к памяти, принятое в системах такого класса и ис­торически сложившиеся традиции или поведение систем, наиболее ожи­даемое разработчиками.
MUMPS системы по своей организации относятся к серверным систе­мам, или, другими словами, к системам серверного класса. При этом они выполняют как задачи сервера баз данных, так и сервера приложений. К ключевым требованиям систем серверного класса относится гаранти­рованное обслуживание заданного числа процессов и выполнение задач в прогнозируемое время.
Для этого система должна обслуживать задачи на ограниченных ре­сурсах как в целом, так и для каждого из выполняемых процессов. К ограничению ресурсов относится использование файлов, сокетов, портов, и оперативной памяти.
В случае, если система для выполнения задачи какого-либо из про­цессов начнет захватывать нерегламентированное или непредусмотрен­ное поставленной задачей количество ресурсов, это может приводить к непредсказуемому переходу к подкачке с диска, что негативно сказы­вается на быстродействии как текущего, так и соседних процессов, и, зачастую, может быть признаком ошибок в исполняемой процессом про­грамме или некорректного отношения к объемам данных.
В MUMPS системах, как и в других системах серверного класса, принято отводить на каждую из областей используемой памяти опреде­ленные пределы. При этом система, выполняя задачи, не выходит за эти пределы.
Выход за пределы расходования памяти может быть двух типов:
1. Процесс захватывает дополнительное пространство памяти для раз­мещения данных.
2. Процесс захватывает дополнительное пространство памяти из-за фрагментации используемого пространства.
Оба случая MUMPS системы стремятся предотвратить и применяют улучшенные алгоритмы повторного использования памяти для снижения фрагментации, а также генерируют ошибку невозможности размещения дополнительных данных в случае исчерпания отведенных пределов.
Традиционно при работе с локальными переменными программист оценивает необходимый программе объем, и обычные переменные для
7.11. ПАМЯТЬ И СБОРКА МУСОРА 499
обработки данных по общему объему находятся в предсказуемых пре­делах. В большинстве случаев достаточно тестового прогона программы для того, чтобы убедиться в достаточности выделенной памяти для ло­кальных переменных.
Кроме предсказуемого и прогнозируемого объема локальных пере­менных могут встречаться случаи использования локальных переменных для хранения временной копии данных, хранящихся в глобалах. В этом случае в локальные переменные могут попасть, вообще говоря, непред­сказуемые объемы, зависящие от того, какие данные и какого объема оказались на текущий момент в базе данных.
В этом случае разработчики должны принять решение и оценить, яв­ляются ли используемые в локальных переменных данные (или могущие в них попасть при обработке) ограниченными по объему. В случае, ес­ли их объем может выходить за разумные для локальных переменных пределы, необходимо в качестве временных переменных использовать глобалы. В отличие от локальных переменных, MUMPS системы могут обрабатывать практически неограниченные, по сравнению с локальными переменными, объемы данных, размещенные в глобалах, из-за примене­ния кеширования и подкачки блоков в кеш по необходимости. В случае с глобалами системе необходимо для одновременного использования лишь несколько блоков в кеше, в то время как для работы с локальными пе­ременными процесс хранит их все в памяти одновременно.
Если разработчики обнаруживают, что административно установлен­ные ими ограничения для локальных переменных недостаточны, пре­делы могут быть изменены. В некоторых реализациях MUMPS систем возможно программное управление объемом локальных переменных для запускаемого процесса, его можно указать в качестве параметра коман­ды job.
Сами по себе MUMPS системы также применяют меры по ограни­чению внутренних пределов памяти для служебных целей, стремясь не выходить на неограниченное ее потребление.
По определению языка MUMPS нигде явно не указано использование указателей или иных структур, фиксирующих объекты языка в памяти. Поэтому, чисто технически, MUMPS системы могут применять внут­ри сборку мусора, хотя большинство систем такого механизма либо не используют либо используют весьма ограниченно. В частности, сборка мусора может быть применена к локальным переменным с ограничением видимости при покидании процессом этой области видимости, либо при удалении локальной переменной.
Механизм сборки мусора, или отложенного возврата использован­ных фрагментов памяти, может приводить к непредсказуемому измене-
500 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
нию производительности из-за срабатывания сборщика мусора и нере­гламентированному приостанову выполнения процесса чтобы дождаться доступности памяти локальных переменных. В таких случаях может на­блюдаться выполнение процесса некоторыми рывками.
Насколько известно автору, современные MUMPS системы таким ме­ханизмом либо не пользуются, либо используют весьма ограниченно и симптомы сборщика мусора, если он присутствует, на практике себя практически не проявляют. В случае если в используемой системе про­являются симптомы сборки мусора и его поведение начинает мешать, рекомендуется обратиться к документации на используемую MUMPS систему или в техподдержку и отрегулировать его поведение.
Кроме выполнения кода, написанного на языке MUMPS, сервера при­ложений или центры интеграции данных могут обращаться к дополни­тельным модулям, написанным на иных средствах разработки, в том числе содержащих встроенные сборщики мусора. В частности, такие среды исполнения как Java и .NET используют сборщик мусора из­начально. При использовании таких систем в качестве динамических библиотек нужно понимать, что в них отношение к используемым про­странствам памяти может отличаться от характерного для серверных систем. В частности, модули могут начать использовать неограниченное или все доступное пространство памяти, или внезапно начать расходо­вать процессор на работу сборщика мусора.
В случае если применяемый модуль, содержащий сборку мусора, на­чинает создавать нехарактерное для серверного поведения препятствие, необходимо обратиться к документации на используемые средства и по­пытаться принять меры к регулированию их поведения или заменить на предсказуемые модули. Во многих случаях, перед использованием в качестве среды модулей расширения систем со сборкой мусора, ре­комендуется тщательно обдумать последствия такого шага и оценить возможность описать функционал модуля на языке MUMPS.
Первоначальные варианты MUMPS систем были ориентированы на работу в качестве серверов баз данных и приложений на весьма скром­ных по нынешним меркам ресурсах, как дисков, так и оперативной па­мяти. Благодаря продуманному отношению к ресурсам вычислительной системы такие сервера весьма уверенно обслуживали задачи, недоступ­ные для систем других типов, работающих на той же аппаратуре. И в настоящее время, при сохранении аккуратного отношения к ресурсам, программные системы, работающие на MUMPS, продолжают характери­зоваться как наименее проблемные и наиболее прогнозируемые в плане отношения к памяти и к времени отклика.
В каждой из MUMPS систем набор административных настроек пре-