Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:MUMPS СУБД. Практика применения и опыт программирования.pdf
X
- •Предисловие
- •Введение
- •Среда исполнения
- •Команды
- •Команды присваивания
- •Условные команды
- •Команды передачи управления
- •Команды ввода-вывода
- •Служебные команды
- •Постусловия
- •Операторы
- •Переменные
- •Числа и строки
- •Функции
- •$DATA
- •$GET
- •$ORDER
- •$NEXT
- •$QUERY
- •$NAME
- •$QLENGTH
- •$QSUBSCRIPT
- •$ASCII
- •$CHAR
- •$EXTRACT
- •$PIECE
- •$LENGTH
- •$REVERSE
- •$FIND
- •$TRANSLATE
- •$JUSTIFY
- •$FNUMBER
- •$TEXT
- •$RANDOM
- •$VIEW
- •$SELECT
- •$STACK
- •Списковые функции
- •Битовые функции
- •Модули
- •Рутины
- •Передача параметров
- •Неопределенные значения
- •Шаблоны
- •Косвенность
- •Косвенность имени
- •Косвенность индексов
- •Косвенность метки
- •Косвенность аргумента
- •Косвенность шаблона
- •Интерпретатор
- •Голая ссылка
- •Очередность выполнения
- •Очередность вычисления выражений
- •Очередность вычисления имен
- •Стекование $test
- •Комментарий
- •Стандарт и расширения
- •Глобалы
- •B-дерево
- •Кодирование индексов
- •Размер блока
- •Кеширование блоков
- •Структуры
- •Индексация
- •Группировка
- •Каноничность индексов
- •Маппинг
- •Индексация данных
- •Общие принципы
- •Механизм поддержки индекса
- •Простой индекс
- •Составной индекс
- •Покрывающий индекс
- •Кластерный индекс
- •Хеш-индекс
- •Битмап индекс (bitmap)
- •Битслайс индекс (bitslice)
- •Нормирование значений
- •Выборки по индексу
- •Многоиндексная выборка (zig-zag)
- •Дифференциальное индексирование
- •Индексация длинных атрибутов
- •Межтабличный индекс
- •Индекс с условием на вставку
- •Индекс на вычисляемый атрибут
- •Индекс поиска по фрагменту
- •Индексация для шаблона (like)
- •Индексация уникального атрибута
- •Массовое перестроение индексов
- •Операции с древовидными индексами
- •Операции с битовыми индексами
- •Сортировка по индексу
- •Статистики и кардинальность
- •Конкурентный доступ
- •Параллельность выполнения
- •Блокировки
- •Функция $INCREMENT
- •Транзакции
- •Блокировки в транзакциях
- •Функция $BIT
- •Дедлоки
- •Обработка ошибок
- •Состояние ошибки
- •ZTRAP
- •GT.M
- •MiniM
- •ETRAP
- •Определение
- •$ETRAP
- •$ECODE
- •$ESTACK
- •Ошибки в обработчике ошибок
- •$STACK()
- •Трассировка
- •BREAK
- •MiniM Debugger
- •Serenji Debugger
- •Внешний мир
- •Общие принципы
- •Терминальный интерфейс
- •Сокеты
- •HTTP клиент
- •Вебсервер на MUMPS
- •WebLink
- •Проблемы HTTP
- •Поверх HTTP
- •Подключаемые DLL (SO)
- •Файлы
- •Внешние процессы
- •Порты
- •Практика применения
- •Терминальный режим
- •Редакторы рутин
- •Экспорт и импорт
- •Препроцессор
- •Формат $HOROLOG
- •Опции устройств
- •$X и $Y
- •Возврат результатов
- •Возврат по значению ($$)
- •Возврат по ссылке
- •Запись в предопределенную переменную
- •Возврат значений косвенно
- •Итеративный возврат
- •Потоковый возврат
- •%Z - рутины
- •Планирование файлов
- •Память и сборка мусора

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 систем набор административных настроек пре-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
