Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
2.8. КАНОНИЧНОСТЬ ИНДЕКСОВ 221
4. Строка не является каноническим числом, если содержит лидирую­щий знак "+" или более чем один знак "-" или знак "-" с одним или более знаками "+" или знак "-" после которого следует лидирующий ноль.
5. Строка не является каноническим числом, если содержит иные бук­вы или цифры, не формирующие экспоненциальную форму числа.
6. Экспоненциальная форма числа не является канонической, если число после представления в виде строки не совпадает с исходной.
7. Значение подходит под определение канонического числа, если за­дано числовой а не строковой константой или вычислено арифме­тически.
Пропустив через такое небольшое сито определений, М система от-
носит строку либо к числам, либо оставляет строкой.
Вот несколько примеров, демонстрирующих различную запись едини­цы, но представляющие различные с точки зрения М систем индексные значения:
USER>s a("1.0")="1.0"
USER>s a("01.")="01.0"
USER>s a("01.0")="01.0"
USER>s a("1.")="1."
USER>s a("1")="1"
USER>s a("01")="01"
USER>s a("-1")="-1"
USER>s a("+1")="+1"
USER>w a(-1)="-1" a(1)="1" a("+1")="+1" a("01")="01" a("01.")="01.0" a("01.0")="01.0" a("1.")="1." a("1.0")="1.0"
222 ГЛАВА 2. ГЛОБАЛЫ
Здесь под определение канонически заданного числа подошли только
две записи:
s a("-1")="-1" s a("1")="1"
Отметим, что большинство М систем при выводе имен переменных отмечает кавычками те индексы, которые она использут как строковые, и без кавычек те, которые использует как числа.
Приведем также пример, показывающий каноничность экспоненци­альной формы:
USER>s a("1e100")="1e100"
USER>s a("1E+100")="1E+100"
USER>w a(1E+100)="1E+100" a("1e100")="1e100"
Здесь каноничной формой М системы считают только второй вариант, с явным указанием знака показателя.
Для выяснения того, является ли строка каноническим представле­нием числа, разработчики на М используют оператор унарного плюс: если применение унарного плюс к строке дает ту же строку, то строка представляет собой каноническое число:
USER>w 1E+100 1E+100 USER>w +"1E+100" 1E+100 USER>w +"1E100" 1E+100
Поэтому, для того чтобы гарантированно использовать в качестве значений индексов именно числа, разработчики используют при индекса­ции унарный плюс. В этом случае М система приводит строку к числу по правилам приведения и используется результат. Зачастую, пользователи при вводе значений могут набрать числа, соответствующие допустимой записи с точки зрения человека, но не каноничное с точки зрения М системы, и унарный плюс канонизирует полученное значение.
Другой особенностью, которую следует учитывать, является возмож­ность формирования логически эквивалентных, но физически различных строк. Есть функции, которые могут дать логически эквивалентные но
2.8. КАНОНИЧНОСТЬ ИНДЕКСОВ 223
физически различные результаты в зависимости от особенностей внут­реннего формата кодирования.
К функциям, формирующим физически различные значения, отно­сятся функции семейства $list. Если элемент списка получен в виде числовой константы либо был вычислен арифметически то функции фор­мируют числовой элемент, иначе строковый. Например:
USER>s list=$lb(123.456)
USER>w $list(list,1)
123.456 USER>s list=$lb("123.456")
USER>w $list(list,1)
123.456 USER>w $lb(123.456)=$lb("123.456") 0
Здесь элементы списка в одних случаях числовые, в других стро­ковые, но во внутреннем кодировании списков формируются различные последовательности байт.
Автору приходилось сталкиваться с использованием списковых струк­тур в качестве составных значений индексов в большой программной си­стеме. Система прекрасно работала до тех пор, пока значения в список попадали, только будучи вычисленными как числа. Как только системе были переданы строковые значения, тут же произошла ошибка. В каче­стве исправления ошибки был выбран отказ от использования списковой структуры в индексе в пользу разделителей. Хотя автор и не уверен, что такое решение проблемы может быть единственным. Нормализация (или канонизация) числовых значений там, где известно, что они должны быть числовыми, а также принудительное приведение чисел к строкам конкатенацией с пустой строкой там, где должны быть строки, также могло бы быть решением.
К функциям, формирующим физически различные последовательно­сти байт для логически эквивалентных значений, также относятся функ­ции $bit, поскольку логический результат проверки бита определяется не только тем, был ли он записан в строку, но и тем правилом, что незаписанные биты рассматриваются как нулевые. Например, различное формирование битовых строк из нулей:
USER>s $bit(bits1,100)=1
USER>s $bit(bits1,100)=0
224 ГЛАВА 2. ГЛОБАЛЫ
USER>s $bit(bits2,10000)=1
USER>s $bit(bits2,10000)=0
USER>w bits1=bits2 0
К общим рекомендациям для разработчиков на М можно добавить рекомендацию не использовать форматы кодирования индексных значе­ний, если они не дают канонического представления, иначе работоспо­собность кода будет зависеть от определенного прикладной системой и комплексом тестов способа попадания данных в систему.

2.9 Маппинг

Маппинг глобалов или отображение глобалов - это механизм замены обращения к глобалу на физическое обращение к глобалу в определенной базе данных или группе файлов (томов).
Маппинг может выполняться на уровне имен глобалов и на уровне индексов. Физическая трансляция имени глобала в другую базу дан­ных может выполняться в глобал с тем же именем, с другим именем, с другими индексами.
Традиционно, маппинг используется разработчиками, как минимум, для так называемых системных и временных глобалов. Большинство со­временых рализаций поддерживает соглашение о так называемых си­стемных рутинах и системных глобалах. В действительности, это обыч­ные глобалы, но для них поддерживается специальное соглашение об отображении на системную базу данных. Традиционно, системные гло­балы и системные рутины первым символом имени имеют символ про­цент (%).
Общепринятое соглашение позволяет различным разработчикам по­нимать друг друга с первого символа имени. Если имя системное, то, из какой бы текущей базы данных к ним ни обратились, физически они (глобалы и рутины) располагаются в одной единственной системной ба­зе данных. Такое соглашение приводит к тому, что системные рутины и глобалы доступны всем процессам в единственном экземпляре, процессы из различных баз данных могут обмениваться данными, и использовать единые для системы рутины общего назначения.
Таким же правилам подчиняются так называемые временные глоба­лы. Для них общеиспользуемого соглашения о формировании имени нет, но принцип тот же - они отображаются в специальную базу данных, к
2.9. МАППИНГ 225
которой применены облегченные настройки записи и журналирования. Временные данные, оставшиеся от прошлого сеанса работы сервера, при его старте могут быть не только удалены, но и база может быть пересо­здана. Для использования временных глобалов и формирования имени такого глобала необходимо обратиться к соответствующей части доку­ментации на используемую М систему.
Маппинг в зависимости от применяемой М системы может быть пред­определенным (как в MiniM), так и полностью настраиваемым (как в Cach´e). Если в MiniM понятие текущей области и текущей базы данных совпадают, то в Cach´e это отдельные понятия. В Cach´e процессы логиче­ски обращаются к области, но область как таковая существует лишь как набор правил отображения глобалов на системную, временную и обычно специально для этой области созданную базу данных.
Cach´e позволяет организовать маппинг весьма сложно и использо­вать множество физически различных баз данных. Нередко применяет­ся конфигурация области, состоящая из отображения части глобалов на системную базу, на временную, на базу для глобалов содержащих дан­ные, и на базу для рутин и специальных справочных данных. При этом база данных с рутинами может передаваться целиком от разработчиков в эксплуатацию, минуя процесс импорта.
При создании новой области средства Cach´e учитывают собственные соглашения об отображении глобалов и рутин по умолчанию и автома­тически создают их для новой области, но эти настройки всегда можно изменить.
Пример маппинга на уровне имени:
^%SRV ^%WM
Здесь процентное имя глобала полностью отображается в системную базу данных.
Пример маппинга на уровне индексов:
^ROUTINE("%RI") ^ROUTINE("%RO") ^ROUTINE("%BACKUP")
Здесь первый символ имени рутины - это процент (%), поэтому вет­ка глобала для хранения рутин ˆROUTINE физически отображается в системную базу данных. Такое же соглашение о маппинге на уровне индексов применяется к глобалам хранящим макрорутины и компилиро­ванный байткод.
226 ГЛАВА 2. ГЛОБАЛЫ
Практически любая многопользовательская СУБД поддерживает в той или иной форме понятие функций и данных общего назначения, или системную базу данных. В системах MUMPS это выполняется по пер­вому символу, или специальным соглашением для временных глобалов.
Разумеется, у разработчика есть возможность всегда обратиться к рутинам или глобалам хранящимся в другой базе данных, задав их место хранения явно, и указав базу данных. Например
^|"%SYS"|COMMON(...) ^|"TEMP"|SELECT($J,...)
В этом случае М система обращается к глобалу в указанной базе, но к используемому имени также применяется правило отображения. Например, имена
^|"USER"|%COMMON ^|"TEMP"|%COMMON
приводят к обращению к базам USER и TEMP, но для них также при­меняются соглашения об отображении системного имени и физически используются глобалы в области %SYS. Такое соглашение позволяет не нарушить правила отображения вне зависимости от того, как были спе­цифицированы имена глобала или рутины.
В зависимости от реализации М системы, если она поддерживает журналирование, различные базы данных могут иметь различные на­стройки журналирования. Разработчикам необходимо учитывать прави­ла настройки журналирования и маппинга для корректной работы прило­жений. Например, чтобы не возникла ситуация что при откате транзак­ций одни глобалы были восстановлены в начальное значение, а другие нет, но приложение использует их взаимозависимо, что и приводит к ошибке.
Глава 3

Индексация данных

3.1 Общие принципы

В этой главе речь пойдет об алгоритмах и структурах данных для ин­дексов, их организации, поддержке и применении.
Термин индекс далее используется строго в целях обозначения допол­нительных поисковых или оптимизирующих структур. Основным языком примеров выбрано стандартное подмножество языка МUMPS. Но, хо­тя по возможности применяется страндартный синтаксис, в некоторых исключительных случаях для большей читаемости применяются Cache Object Script и MiniM Database Server - расширения. Их применение ограничено и допускает альтернативную замену на эквивалентные выра­жения в иных диалектах МUMPS. Применение битмап индексов огра­ничено теми MUMPS системами, которые поддерживают расширенные $BIT функции.
Индексы - это структуры данных, размещаемые параллельно и под­держиваемые синхронно основным структурам данных и имеющие основ­ным назначением поддержание структур данных, ориентированных на ускорение поиска или оптимизацию хранения основных данных. Здесь под основными данными понимаются данные, хранение и работа с кото­рыми является основным назначением системы базы данных.
При использовании основных данных система базы данных выпол­няет операции вставки, поиска, удаления и изменения в общем масси­ве их хранения. При использовании дополнительных индексных струк­тур система параллельно обновляет индексные структуры при изменении (вставке, изменении и удалении) основных данных и в некоторых слу­чаях получает возможность использовать индексные структуры, ориен­тированные на поиск данных. Наличие такой возможности определяется
227
228 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
характеристиками и структурой индекса.
Как следует из вышеприведенного, введение индексов в систему базы данных утяжеляет операции, связанные с изменением данных, но уско­ряет операции связанные с поиском и, как обычно, в следствии этого, с выборкой данных.
Индексные структуры сами по себе обычно не являются необходи­мыми для основной работы системы базы данных. И их применение определяется программистом или администратором системы.
В большинстве общераспространенных систем баз данных поддержка индексных структур и их использование выполняется автоматическими средствами. В этой главе мы будем составлять структуры и алгорит­мы, которые можно использовать вне автоматики и пользоваться все­ми возможностями безотносительно ограничений системы базы данных. Примерно как если бы по частям реализовали внутренние механизмы большой системы, но в несколько упрощенном варианте.

3.2 Механизм поддержки индекса

Индексная структура по своему состоянию должна соответствовать со­стоянию индексируемых данных. Поэтому операции обновления индек­сов обычно делят на две группы - динамическое обновление индексных структур при обновлении одной записи и массовые операции удаления / построения индексов.
Далее будем рассматривать строки данных, устроенные для простоты следующим образом:
1. Идентификатор записи получаем инкрементом узла ˆData
2. Значение записи хранится в узле ˆData(id)
3. Запись состоит из полей с разделителем ˜ (тильда)
4. Индексные записи храним с глобале ˆIndex
5. В записи предполагаем поля - фигура, цвет, количество
6. Общее строение записи: ˆData(id)=Figure˜Color˜Count
Операции динамического обновления индексов могут вызываться из операции обновления записи, и либо предшествовать собственно сохра­нению основной записи, либо последовать ему, либо обрамлять.
Например:
3.2. МЕХАНИЗМ ПОДДЕРЖКИ ИНДЕКСА 229
; просто сохранение объекта
SaveObject(id,ObjVal)
i ’+$g(id) s id=$i(^Data) s ^Data(id)=ObjVal q ; обновление индексов перед сохранением
SaveObject(id,ObjVal)
n OldValue i ’+$g(id) s id=$i(^Data) s OldValue=$g(^Data(id)) d DeleteIndices(id,OldValue) d InsertIndices(id,ObjVal) s ^Data(id)=ObjVal q ; обновление индексов после сохранения
SaveObject(id,ObjVal)
n OldValue i ’+$g(id) s id=$i(^Data) s OldValue=$g(^Data(id)) s ^Data(id)=ObjVal d DeleteIndices(id,OldValue) d InsertIndices(id,ObjVal) q ; обрамление обновления индексов при сохранении
SaveObject(id,ObjVal)
i ’+$g(id) s id=$i(^Data) d DeleteIndices(id,$g(^Data(id))) s ^Data(id)=ObjVal d InsertIndices(id,ObjVal) q
Здесь DeletIndices удаляет индексные записи по этому объекту, а InsertIndices их создает. В данном случае подразумевается простой фор­мат хранения записи - одной строкой, которая трактуется либо как стро­ка содержащая одно значение.
Несмотря на то, что три метода в итоге дают одинаковый резуль­тат, между ними есть разница в том, насколько правильно будет рабо­тать конкурентный (одновременный для нескольких процессов) доступ к данным и индексам. В случае хранения только данных этот вопрос прак­тически не стоит, поскольку операция set атомарная в том смысле, что в операции выполняется только одно изменение в глобалах. В случае же применения параллельных структур индексов существует момент между состояниями, когда записи нет, но индекс есть, или наоборот, индекс есть но записи нет. Этот вопрос решается обычно с помощью приме­нения блокировок. Операция set нового значения записи обрамляется командами
230 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
l +^Data(id) s ^Data(id)=ObjVal l -^Data(id)
И внутри функций удаления / вставки индексных записей также вставляются обрамляющие блокировки. Наличие блокировок особенно критично в случае исполнения кода в контексте транзакции и возмож­ности выполнения операции trollback.
Различие в режиме перестроения индекса, а именно что раньше по­явится в базе - индексная запись или запись с данными, позволяет по­строить в некотором смысле самовосстанавливающуюся систему, кото­рая будет иметь возможность восстановиться в случае сбоя при записи строки данных. Если индекс построен раньше, то при выборке по индек­су функция выборки данных может определить, что индексная запись существует, но ей не соответствует строка данных.
В случае применения блокировок в операции обновления записи мы в функции выборки можем также попытаться заблокировать эту же запись и, если блокировка оказалась успешной, но записи нет, или ее состояние не соответствует индексным значениям, то значит что операция записи самой строки данных была неуспешной и следует просто удалить ин­дексную запись. Механизм довольно громоздкий, но в ситуации, когда из соображений эффективности не хочется применять транзакции, мо­жет оказаться полезным. Вопрос выбора стратегии обновления индекса при обновлении записи оставим программисту.
Операция перестроения индекса сводится к удалению всех индексных записей и перебору всех имеющихся записей с данными и построения индексных записей по каждой имеющейся записи данных. Полагаем, что есть функции DeleteIndex для удаления всех индексных записей по одному индексу. Тогда перестроение индекса может выглядеть как
UpdateIndex(IndexName)
d DeleteIndex(IndexName) n id,ObjValue s id="" f s id=$o(^Data(id),ObjValue) q:id="" d . d InsertIndex(IndexName,id,ObjVal) q

3.3 Простой индекс

Простой индекс в некоторой литературе ещё называется обратным спис­ком. Если структуры основных данных отображают идентификатор за­писи (назначенный программистом или поддерживаемый автоматически