Добавил:
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 - рутины
- •Планирование файлов
- •Память и сборка мусора

3.26. СТАТИСТИКИ И КАРДИНАЛЬНОСТЬ 321
по атрибуту даты и изделия, чтобы выбрать вариант, имеющий наибольшее сужение первичной области поиска.
Пусть в начале жизни системы мы ввели два документа и в каждом
по пять строк с различными изделиями. В этом состоянии индекс по дате
содержит два различных значения, индекс по изделию содержит десять
различных значений. В данной ситуации оптимизатор, скорее всего, выберет второй вариант - использовать сначала индекс по изделию, потом
индекс по дате с пересечением.
Со временем в систему вводятся новые документы. Каждый день число различных значений дат документов увеличивается, но число различных изделий в строках документов ограничено спецификой документов
и изделий, и со временем растет все меньше, и через некоторое время
перестает расти. Через некоторое время наступает момент, когда число
различных значений дат документов становится больше, чем различных
изделий в строках документов. Но, если статистики не обновлены и
отражают начальное состояние, то оптимизатор по инерции может выбирать все тот же план. Если выполнить перестроение статистик, то
при следующем таком запросе оптимизатор уже может изменить план
выполнения на более быстродействующий.
Это, в целом, упрощенная ситуация с использованием статистик автоматическим оптимизатором. В действительности современные оптимизаторы используют большое количество низкоуровневой информации, как
например распределение записей по блокам и эффективности кеширования индексных записей, частота использования индекса и вероятность
его кеширования, и другие.
При программировании MUMPS систем, традиционно, программисты не используют автоматических оптимизаторов и пишут код выборки в традиционном навигационном стиле. При этом, в отличие от автоматического режима, есть возможность попасть в противоположную
неприятность - не учтя статистик данных, использовать план выборки,
который уже не сможет быть изменен без перепрограммирования. Но,
одновременно с тем, при правильном выборе плана система будет работать намного более стабильно и предсказуемо, не будет зависеть от
таинственных и неуправляемых факторов. Иллюстрируя вышеприведенным примером, разработчики могут изначально ориентироваться на то,
чего будет больше в реальной системе - различных дат или различных
изделий.
Кроме того, при прямом доступе есть возможность использования
сложных индексов, использовать которые автоматический оптимизатор
врядли сможет, такие как, скажем, индексы с условием на вставку или
индексы для поиска по фрагменту.

322 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
Что касается именно приведенного примера, то, в отличие от табличноориентированных систем с автоматическим поддержанием индексов, более эффективным вариантом может оказаться поддержание межтабличного индекса, объединяющего изделие в строке документа и дату документа и возвращающего пару - идентификатор документа и номер строки документа, или внутри табличный индекс, отображающий изделие на
внешний ключ - идентификатор документа, или, что проще, составной
индекс на атрибуты изделие и внешний ключ. В последнем случае, если
автоматический оптимизатор увидит покрывающие свойства такого индекса и сможет использовать многоиндексную выборку (zig-zag ordered
scan), то это будет, вероятно, хорошим решением.
При применении MUMPS систем, в отличие от систем с автоматическими оптимизаторами, работающими по декларативно заданным запросам, присутствуют, таким образом, как плюсы, так и минусы. К минусам
можно отнести то, что при разработке прикладных систем необходимо
уделить определенное внимание и затратить некоторые усилия на введение в систему индексов. К плюсам можно отнести то, что у разработчиков прикладных систем на MUMPS практически отсутствуют ограничения и на типы возможных к применению индексов, и на алгоритмы
их поддержки и использования. Хотя базовый набор действий выполняемых MUMPS системой прост, на нем возможно построение от самых
простых до самых сложных систем.

Глава 4
Конкурентный доступ
4.1 Параллельность выполнения
MUMPS системы изначально предусматривают выполнение чего-либо в
рамках процессов (называемых job, или задание), существующих в контексте М системы. Процесс может быть запущен, выполняться, или завершить работу. Если на компьютере установлено и работает несколько
М систем, то в контексте каждой из них набор процессов и их окружение
будут собственными.
Физический способ реализации процесса по стандарту MUMPS не
лимитирован никаким образом, и, в зависимости от реализации, это
могут быть действительно процессы операционной системы, или потоки,
или собственный механизм переключения контекстов. Стандарт MUMPS
преполагает, что М система выполняет процессы параллельно, хотя в
силу особенностей реализации физический способ такой параллельности
может отличаться.
Примеры физически различной реализации процессов M системы:
1. В MiniM и Cach´e M процесс соответствует процессу операционной
системы
2. В MiniMono и M3-Lite M процесс соответствует потоку операционной системы
3. В MSM-DOS M процесс соответствует внутреннему виртуальному
контексту
Каждый из процессов М системы имеет собственный текущий номер.
Этот номер возвращается системной переменной $JOB. При старте процесса М система назначает ему номер не совпадающий ни с одним уже
323

324 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
имеющимся таким образом, что никакие два выполняющиеся на одной
М системе (это не относится к процессам нескольких М систем на том
же компьютере) процесса не имеют одинакового номера.
Номер процесса, полученный им при старте, не изменяется в течении
всего времени его работы.
Собственно само значение переменной $JOB также не определено
по формату, это может быть сочетание цифр или букв. Это может быть
длинное или короткое число. Но в любом случае это число отличает
текущий процесс М системы от любых процессов этой же системы.
Вообще говоря, после завершения M процесса его номер процесса
может быть использован повторно при старте другого M процесса, но
это необязательное условие. В частности, в системе M3-Lite выделение процессам номеров никак не привязано ни к номерам процессов
операционной системы, ни к номерам потоков операционной системы, а
выделяется последовательно.
Физическое соответствие значения переменной $JOB объектам операционной системы также зависит от реализации М системы:
1. В MiniM и Cach´e $JOB соответствует номеру процесса операционной системы
2. В MiniMono $JOB соответствует номеру потока операционной системы
3. В M3-Lite и MSM-DOS $JOB соответствует внутреннему виртуальному номеру
Разработчикам М программ, таким образом, не следует полагать, что
номер $JOB связан с каким-либо объектом операционной системы. Более того, значение номеров $JOB и способ их выделения процессу может
меняться от версии к версии одного производителя.
Согласно стандарта MUMPS, все имеющиеся в М системе процессы
перечисляются в структурной системной переменной ˆ$JOB в качестве
индексов первого уровня и в отношении этой переменной должен поддерживаться минимальный набор операций, такие как $ORDER и $DATA.
Например, перечислить имеющиеся в М системе процессы можно
так:
s j="" f s j=$o(^$J(j)) q:j="" w j,!
Кроме операций предусмотренных стандартом, различные реализации М систем могут дополнительно поддерживать расширенный набор операций со структурными системными переменными, например в
MiniM поддерживается операция

4.1. ПАРАЛЛЕЛЬНОСТЬ ВЫПОЛНЕНИЯ 325
kill ^$JOB(job)
принудительно завершающая выполнение указанного процесса.
Для разработчиков на М важным моментом является характер выполнения процессов и переключение их контекстов и характер чтения
значений переменных.
Формально, стандарт MUMPS никак не регламентирует характер переключения контекстов М процессов при выполнении программ и отдельных команд или функций. При выполнении кода, состоящего из
вызовов функций, чтения и записи переменных и передаче управления
от команды к команде, в любой момент времени может произойти переключение контекста процессора на другой процесс. Если есть, например,
команда
set a=a+1
то процесс может прочитать значение переменной a, после чего контекст выполнения процессора переключится на другой М процесс, затем
продолжится выполнение с операции сложения, затем снова прервется и затем продолжится операцией записи. Те же самые соглашения о
неопределенности прерывания выполнения для переключения контекстов
касаются и других многозадачных систем и средств программирования.
Но, одновременно с тем, что М системы выполняют процессы параллельно, для них, как для процессов СУБД, отдельно определено дополнительное соглашение об атомарности обращений к переменным.
Для каждого процесса в М системах определены 3 вида переменых:
1. Локальные переменные, принадлежащие и видимые только текущему процессу.
2. Глобальные переменные, принадлежащие М системе в целом и видимые всем процессам.
3. Системные и структурные системные переменные, существующие
у каждого процесса, но значение которых определяется для каждой
переменной индивидуально.
Для каждого из этих видов переменных определено, что процесс
получает значение переменной целиком. В отличие от других средств
разработки, в М системах может быть предусмотрена возможность получить значения локальных переменных одного процесса другим процессом и чтение состояния его системных переменных. Это делается в
целях отладки, например, или при выполнении административных задач.

326 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
Атомарность доступа к значениям переменных означает, что если переменная имеет какое-то значение, то процесс читает его целиком. Если
процесс изменяет значение, то оно заменяется целиком. Например, пусть
два процесса выполняют операции
первый
s ^A="1111111111111111111111"
второй
s ^A="2222222222222222222222"
При этом соглашение об атомарности доступа приводит к тому, что
третий процесс прочитает либо значение из всех единиц, либо из всех
двоек, но никогда не прочитает смесь из байт, записанных разными
процессами.
Отдельные функции языка выглядят так что они как будто меняют
только часть переменной, например
set $extract(^A,4,7)=1234
В действительности, для таких функций не определено, как именно
М система будет выполнять конкурентный доступ. Есть два варианта
выполнения, первый - выполняется сначала чтение значения, потом его
модификация во временной памяти, и затем запись строки опять же целиком. Но параллельность выполнения процессов приводит к тому, что
между операцией чтения и записи может произойти перезапись этой
же переменной другим процессом. Второй вариант - система блокирует
доступ к этой переменной, не давая другим процессам изменить переменную, выполняет изменение и отпускает блокировку. Большинство М
систем реализует первый вариант. Те системы баз данных, которые используют второй вариант, обрекают себя на возможность коллизий взаимоблокировок, если для блокирований используют те же механизмы
блокировок, которые используются программистами.
Что интересно в М системах, это то, что паралельность выполнения
команд и функций различными М процессами обеспечивается вне зависимости от физической реализации системы и от типа базовой операционной системы. В любом случае разработчикам предоставляется набор
соглашений о поведении таких виртуальных процессов и стандартные
соглашения о переносимости программ, даже если целевая M система
работает в однозадачной операционной среде.

4.2. БЛОКИРОВКИ 327
4.2 Блокировки
Для обеспечения корректности одновременного доступа различных М
процессов в условиях параллельности их выполнения М системы реализуют различного рода блокировки. Часть этих блокировок выполняется
автоматически внутренними механизмами системы, часть предусмотрены стандартом языка и предоставлены разработчикам.
Блокировка - это независимо от других объектов существующая запись в контексте М системы. Блокировки не хранимы на дисках, а существуют в специально отведенной области памяти М системы. Формально
говоря, запрета на хранение блокировок в файле на диске нет, но все
имеющиеся M системы используют только блокировки, хранящиеся в
оперативной памяти.
При блокировании указывается имя, структурно соответствующее
имени локальной или глобальной переменной. Блокирование выполняется командой блокировки lock, например:
lock abc(123)
lock ^ABC(456)
lock ^|"%SYS"|COMMON(789)
Для большинства М систем нет разницы между локальными и глобальными блокировками. Обычно разработчики выбирают, какие имена
необходимо блокировать - локальные или глобальные и далее пользуются внутренними соглашениями.
При блокировании локальных и глобальных имен, даже если они
совпадают, производятся различные блокирования, поскольку локальное
имя не равно глобальному.
Различие между локальными и глобальными именами блокировок
возникает при использовании распределенных систем, когда сервера М
систем объединены и процессы одной М системы могут использовать базу данных другой, удаленной М системы. В этом случае блокирование
глобального имени, соответствующего удаленной системе, выполняется
на том сервере, где должна храниться эта глобальная переменная, если
бы она существовала. При этом блокировки с локальным именем будут храниться на том сервере, где выполняется процесс. И в этих двух
случаях М системы ищут блокировки в разных областях, так как они
находятся на разных серверах.
Поэтому, если два процесса должны синхронизироваться по доступу
к глобалу, то обычно выбирается блокирование глобального имени, а если должны синхронизироваться по доступу к строго локальному ресурсу,
то выбирается блокирование локального имени.

328 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
Несмотря на то, что аргументом блокировки является локальное или
глобальное имя, это имя никак не связано с точно таким же именем
локальной или глобальной переменной соответственно. Это разные объекты М систем. Наличие или отсутствие блокировки, установленной в
одном из процессов, другим процессам никак не мешает получить доступ
к этой переменной чтобы прочитать или записать, а также не требуется
само существование таких переменных.
Блокировки необходимы лишь для взаимной синхронизации выполнения процессов. То есть два или более процесса, которые должны иметь
корректный доступ к глобалам, должны установить необходимые блокировки, а потом их снять. Или, другими словами, управление должно
пройти через команды блокирования.
Для блокировок в М системах действует простое правило: два или
более процессов не могут захватить блокировки имен, если эти имена являются вложениями друг друга в иерархии дерева имени или в точности
совпадают. Не имеет значения, какое из имен было захвачено первым,
более длинное или более короткое. Иначе могут.
Примеры взаимоисключающих блокировок:
^ABC и ^ABC(123)
^DATA и ^DATA
Здесь в первом случае имена находятся в отношении иерархии, во
втором случае совпадают.
Примеры взиморазрешающих блокировок:
^ABC и ^DATA
^DATA(3) и ^DATA(5)
Здесь в первом случае имена не совпадают, во втором случае имена
не находятся в отношении иерархии.
В частности, из соглашений об именовании блокировок вытекает возможность реализовать блокирование на чтение-запись. Это такой вариант блокирования объекта, при котором доступ может получить либо
лишь один пишущий процесс и никто более, либо один или более читающий и ни одного пишущего.
Для реализации такой стратегии для пишущего процесса блокируемое имя совпадает с именем ресурса, а для читающего добавляется к
имени номер его процесса.
Имя ресурса:
^DATA("year",45,"details")

4.2. БЛОКИРОВКИ 329
Блокирование на запись:
lock ^DATA("year",45,"details")
Блокирование на чтение:
lock ^DATA("year",45,"details",$J)
Здесь каждый из блокирующих на чтение использует имя, параллельное другому блокирующему на чтение и эти имена находятся в иерархии
с именем блокирования на запись.
При необходимости выполнить синхронизацию доступа к ресурсам,
не являющимся глобалами, разработчикам необходимо договориться о
трансформации имени ресурса в локальное или глобальное и использовать уже их.
Как именно выполняется синхронизация процесса: команда блокирования lock проверяет возможность выполнить блокирование и в случае возможности вносит в имена блокировок новую блокировку, процесс
продолжает выполнение. Если блокирование невозможно и одна или более имеющихся блокировок запрещают существование новой, то команда lock приостанавливает выполнение процесса до наступления условия
возможности блокирования либо до истечения времени ожидания блокировки, если оно было установлено.
Блокировки выполняют как бы операторные скобки окружения таким образом, что между командами lock выполняемый процессом код
гарантирован от того, что другой процесс получит доступ к тем же переменным. Разумеется, оба процесса должны использовать одинаковые
соглашения об именах блокировок. Например, при выполнении кода
lock ^A
set value=^A
set value=value+1
set ^A=value
lock
для каждого из процессов, использующих синхронизацию, гарантировано получение следующего номера, для каждого из процессов своего, и не
получится так что пока один процесс вычислял новое значение, другой
уже также перезаписал его, вычислив на основе того же значения, и оба
бы в результате использовали одинаковое значение value.
Команда блокирования имеет два варианта - полную и инкрементную
форму. В вышеприведенных примерах использовалась полная форма. В
этом варианте перед тем как выполнить блокирование указанного имени команда снимает все имевшиеся блокировки. Например, при первом
прочтении кода

330 ГЛАВА 4. КОНКУРЕНТНЫЙ ДОСТУП
lock ^A,^B,^C
может показаться, что команда применена к трем именам и после ее
выполнения будут блокированы все три имени, но это не так. Сначала
команда снимет все блокировки, потом выставит блокировку на ˆA, затем снимет ее, выставит блокировку на ˆB, затем снимет ее и выставит
блокировку на ˆC. И в результате будут блокированы не имена ˆA + ˆB
+ ˆC, а только имя ˆC.
Для того, чтобы снять все блокировки, используется безаргументная
форма команды:
lock
поэтому надо быть внимательным к числу пробелов, поскольку последующее имя может оказаться синтаксически значимой для М конструкцией.
Для того, чтобы добавлять или снимать блокировки независимо от
уже существующих имен блокировок, команда lock используется в инкрементной форме. Для добавления блокировки инкрементно перед именем ставится символ +, для снятия инкрементно перед именем ставится
символ -. Например
lock +^A
set value=^A
set value=value+1
set ^A=value
lock -^A
В этом случае блокирование имени ˆA никак не затрагивает остальные блокированные имена.
Что интересно, инкрементная форма блокирования использует внутренний счетчик блокирования таким образом, что инкрементная блокировка при первом блокировании создает имя со счетчиком 1, а при
последующих увеличивает счетчик этого имени на 1. При инкрементном
снятии блокировки счетчик уменьшается на 1. Если счетчик становится
равен 0, то блокировка снимается совсем и блокируемое имя удаляется
из имен блокировок.
Такое определение поведения инкрементных блокировок позволяет
создавать библиотечные подпрограммы, которые должны оперировать
лишь определенной им частью системы, не затрагивая остальную.
По своему общему назначению блокировка - это объект синхронизации процессов. Для того, чтобы определить необходимо ли использовать
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
