Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
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, то блокировка снимается совсем и блокируемое имя удаляется из имен блокировок.
Такое определение поведения инкрементных блокировок позволяет создавать библиотечные подпрограммы, которые должны оперировать лишь определенной им частью системы, не затрагивая остальную.
По своему общему назначению блокировка - это объект синхрониза­ции процессов. Для того, чтобы определить необходимо ли использовать