Добавил:
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.19. ИНДЕКСАЦИЯ ДЛЯ ШАБЛОНА (LIKE) 291
s id="" f s id=$o(ids(id)) q:id="" d
. i ^LIKEDATA(id)’?@pat k ids(id)
И, наконец, контрольный тест для запуска:
test ;
n ids,like
f like="* *","*ef","*hj?" d
. k ids
. d LIKESELECT(like,.ids)
. d dump
q
dump
w "like template=""",like,"""",!
n id="" f s id=$o(ids(id)) q:id="" d
. w "found id=",id,", string=""",^LIKEDATA(id),"""",!
q
При необходимости разработчик может вставить трассировку выполнения или счетчики операций и, последовательно совершенствуя алгоритм, добиться большей оптимизации, например выполнять поиск по
индексу не в индексной сортировке, а использовать сначала отсечение
по фрагментам максимальной длины, например построить структуру
^LIKEDATA(id)=word
^LIKEIND(len,part,id)=""
Здесь id - идентификатор слова, word - значение слова, part - фрагмент слова, len - длина фрагмента.
Соответственно может быть модифицировано и применение алгоритма поиска $$ANDnext() с тем, чтобы он выполнял проход сначала по
самым длинным фрагментам.
Кроме того, каждый разработчик видит проблему по-своему, и у каждого могут быть свои варианты, как построить индексацию для поиска по
шаблону, чтобы она выполнялась максимально быстро. Вполне возможно, что разработчик может использовать специфику разрабатываемой
системы и / или особенности индексируемых данных.
Интересно отметить, что у поиска по шаблону может быть найдена масса вариаций. В одном из проектов, разработанных на MUMPS с
прямым программированием индексных структур и нестандартными выборками из индексов было применено решение, содержащее разбиение
значения атрибута на фрагменты и построение индексов по фрагментам.
Далее, при вводе искомой строки она также разбивалась на фрагменты. Поиск выполнялся по начальным частям полученных фрагментов и

292 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
нечувствительно к регистру. Можно было ввести одну - две буквы из
начала каждого из необходимых фрагментов и система автоматически
и весьма быстро находила в огромном массиве те записи, в которых
присутствовали фрагменты начинающиеся на те же символы.
Такой поиск решал проблему поиска по названиям или именам, когда
одно название или полное имя может включать много слов, но затруднительно набрать в тоности то же самое, что было введено при записи
данных в базу. В частности, название
ООО "Белый Медведь"
может быть введено в базу данных в разном виде, но логически эти
названия эквивалентны для пользователя:
ООО "Белый Медведь"
ООО Фирма "Белый Медведь"
Белый Медведь, ООО
ООО БЕЛЫЙ МЕДВЕДЬ
После разбиения на отдельные слова и приведения к общему регистру
в индексах системы использовались значения
OOO
БЕЛЫЙ
МЕДВЕДЬ
Разумеется, что при применении такого механизма поиска, когда
пользователь мог просто ввести в строке поиска
бе ме
система оперативно отыскивала также и необходимое ему название ООО
"Белый Медведь", и, возможно, еще пару подходящих под такой же
шаблон.
Скорость поиска по описанным алгоритмам такова, что поиск первых
10 - 20 строк может выполняться интерактивно, при очередном вводе
символа в строке поиска.
3.20 Индексация уникального атрибута
Одной из интереснейших и спорных тем в индексации является тема индексации уникального атрибута. Индексы в данном случае используются
по меньшей мере в двух различных задачах:

3.20. ИНДЕКСАЦИЯ УНИКАЛЬНОГО АТРИБУТА 293
1. Для поиска нужной записи по заданному значению атрибута.
2. Для поддержания условия уникальности значения атрибута среди
всех имеющихся записей.
Сам факт использования уникальных значений атрибутов вызывает
споры, подобные религиозным - использовать ли их в качестве естественных ключей или нет, и на каком уровне реализации системы должна происходить поддержка уникальности - на уровне сервера приложения (прикладная логика) или на уровне сервера базы данных (нижний
уровень хранения). Чтобы не вызывать не относящихся к теме вопроса
противоречий, выберем условный пример, с которым могут согласиться
сторонники различных подходов. Положим, что у нас есть учетная система, в которой сохраняются сведения о пользователях и нам предстоит
сохранить в ней атрибуты "номер паспорта" и "логин".
Оба атрибута всегда должны быть уникальны среди всех записей,
никак не связаны друг с другом, и номер паспорта может со временем
меняться. Кажется очевидным, что при удалении записи пользователя из
системы его логин впоследствии также не может быть использован повторно, и что у одного пользователя могут быть два или более паспортов,
с единственным ограничением, что только один из них является применимым для совершения новой сделки. Назовем его текущим активным.
Кроме того, пользователь системы может перестать в ней существовать
в том смысле, что с одной стороны он может быть как-бы удален, с другой стороны имеющиеся на него ссылки должны остаться действующими
для расшифровки имевшихся на какой-то предыдущий момент времени
в системе взаимосвязей.
Чтобы не возникло желания использовать логин в качестве естественного ключа (догматический подход на практике обычно не работает). Мы наложим еще одно условие - логин также может со временем
меняться, также как номер паспорта, логин не может быть впоследствии
повторен и всегда должен быть уникален среди всех логинов, известных
системе. Приведенный пример выглядит вполне разумным для того, чтобы согласиться с мыслью о правомерности существования уникальных
атрибутов.
Чтобы жизнь медом не казалась, придумаем еще один уникальный
атрибут - номер служебного мобильного телефона, выдаваемого фирмой
своему сотруднику. Номер может со временем быть передан другому
сотруднику, он может отсутствовать, и одновременно не может принадлежать двум сотрудникам. В качестве значения такого атрибута нам без
разницы что использовать - сам номер или идентификатор записи об

294 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
отдельном объекте учета. В любом случае среди всех записей о пользователях заданный номер телефона может или не быть ни у кого, или
быть только у одного сотрудника.
В примере номер телефона, наверное, наиболее типичный пример
уникального атрибута. Часть схемы данных о пользователе будет такой:
Запись о сотруднике: ^D(iduser)=$lb(phone)
Индексная запись: ^I(phone,iduser)=""
На каком уровне будет проводиться проверка уникальности атрибута,
на уровне логики или на уровне хранения, видимо, не принципиально,
поскольку действия в принципе должны быть выполнены одни и те же.
Рассмотрим, какие операции могут привести к модификации записи
и какие из них к нарушению уникальности атрибута:
Вставка новой записи
При вставке может возникнуть конфликт уникальности, если такое
значение уже было. Если не было, то вставка безопасна.
Удаление имеющейся записи
Если запись была, то ее удаление гарантированно оставляет условие "либо один либо ноль" истинным, поэтому удаление записи всегда
безопасно с точки зрения уникальности атрибута.
Изменение атрибута
При изменении атрибута может произойти конфликт, если новое значение уже существует. Если такого не было, то изменение безопасно.
Откат транзакции
При откате транзакции сервер базы данных восстанавливает предыдущее значение. В случае, если оно было (операции удаления и изменения), то сервер базы данных самостоятельно выставит значения данным,
не согласуясь с поддерживаемым нами ограничением уникальности. Для
того, чтобы обезопасить систему от нарушения уникальности при откате
транзакции с изменением значения, необходимо применять блокировки.
В целом, уникальность значения атрибута есть лишь один из очень
простых видов ограничений целостности базы данных - они могут быть
объявлены гораздо более комплексно и затрагивать большое количество
сущностей в весьма сложной взаимосвязи.
Для того, чтобы гарантировать, что значение всегда одно, может быть
использован индекс вида
^Index(IndexName,Value)=id
В этом случае в индексе физически не может одновременно существовать две записи с разным id.

3.21. МАССОВОЕ ПЕРЕСТРОЕНИЕ ИНДЕКСОВ 295
Вне зависимости от использования механизмов транзакций, разработчик должен применять блокировки, если налагаются условия не на
отдельное значение атрибута, а на его зависимость от значений атрибутов других записей. Проверять и модифицировать записи, если значения
атрибутов взаимозависимы с другими записями можно, только если наложена блокировка, означающая, что это условие проверяется текущим
процессом. Блокировки могут быть выполнены как монопольно, так и с
различением блокирования на операции чтения и записи в зависимости
от выполняемой операции.
Имя блокировки не обязано соответствовать имени глобала индекса,
это может быть в целом любое имя, главное чтобы система именования
отображала блокируемое условие. Зачастую строение индексного глобала достаточно близко к имени блокировки, и именно его разработчики и
используют в типовых задачах.
Поддержание уникальности значения атрибута есть частный случай
наложения условий на взаимные значения различных записей. В частности, при традиционной трактовке уникальность означает существование
записи с данным значением атрибута в количестве от 0 до 1. Значение 1
может быть лишь частным случаем, одним из значений для более общего параметра уникальности N . При замене N на, например, 7, получаем
условие чтобы указанное значение присутствовало в записях числом от
0 до 7.
При выполнении таких более общих условий уникальности в других, не MUMPS системах, необходимо приводить условие к условию
простой уникальности. Например, завести отдельный справочник допустимых значений, и ссылаться на записи из этого справочника, но с
ограничением что на каждую запись справочника может сослаться лишь
одна запись. Например, если необходимо выполнить условие "не более
7 синих одновременно", то придется завести 7 записей в справочнике с
значением "синий" и ссылаться на такие записи, поддерживая особую
дисциплину добавления и удаления записей в справочнике административно. Кроме того, понадобится реализовать стратегию монопольного
захвата одной из свободных записей справочника, например с применением опций
SELECT ... FOR UPDATE
3.21 Массовое перестроение индексов
Операция поддержания индексов в актуальном состоянии обычно состоит в обновлении индексных структур при изменении основных, индекси-

296 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
руемых. Некоторым особняком стоит операция массового перестроения
индекса по каким-либо причинам. При ее исполнении перестраиваются
индексные записи для одновременно большого числа записей. В эксплуатационном отношении это массовое перестроение может быть оптимизировано как программно, так и административно (если такая возможность
предусмотрена).
К массовому перестроению индексов / одного индекса приводят за-
дачи:
Создание индекса
При создании индекса СУБД должна для каждой имеющейся индексируемой записи выполнить вставку индексных записей. При этом, пока
идет их вставка, сами данные не изменяются.
Изменение кластерного индекса
При изменении кластерного индекса все записи меняют идентификаторы, поэтому все другие индексные записи должны быть перестроены.
При перестроении записей сами данные не изменяются.
Массовый импорт данных
Обычно перед импортом данных индексы отключают, если есть такая
возможность, проводят импорт, потом включают индекс. Пока индекс
выключен, он просто накапливает информацию о необходимости переиндексации определенных записей - вместо внесения индексных структур делается отметка о том, что после включения индекса может быть
не полное перестроение индексных записей, а только для измененных
/ добавленных / удаленных записей. После включения (или активации)
индекса перестроение идет не по всем записям, а только по тем, о которых есть отметка.
Удаление данных таблицы
При удалении индексируемых записей СУБД может выполнить как
позаписное удаление (delete * from table), так и массовое освобождение
занимаемого пространства (truncate table). В случае М-систем полного
аналога truncate не существует, поэтому оптимизировать можно лишь
удаление индексных структур - при полном удалении данных таблиц
полностью удалять все индексные деревья.
Возможное изменение структуры индексируемых данных
Если в структуре индексных записей как-то использовалась информация о структуре индексируемой записи, то в случае ее изменения такие
индексы также должны быть перестроены - имеющиеся записи удалены
и построены новые.
Изменение определения индекса
При поддержании индекса с условием на вставку или индекса с вычисляемым значением это условие и выражение вычисления могут изме-

3.21. МАССОВОЕ ПЕРЕСТРОЕНИЕ ИНДЕКСОВ 297
ниться. В этом случае все индексные записи по такому индексу должны
быть удалены и построены заново. В целях оптимизации по времени
выполнения операции массового перестроения индексов мы можем использовать специфические для этих операций признаки:
1. Индексируемые данные не меняются. При этом мы выполняем цикл.
Следовательно, у нас могут оказаться инварианты цикла, которые
мы можем вынести за пределы цикла. Например, мы можем эскалировать блокировки до более общих, сэкономить на создании и
очистке временных переменных и так далее. Вынос инварианта за
пределы цикла - это оптимизационная задача, и она может быть
выполнена обычно только после составления самого кода, который
нужно оптимизировать.
2. При массовой вставке индексных записей мы предполагаем, что
эта операция может быть длительной и затронуть значительное количество узлов. Поэтому мы может использовать наиболее общую
блокировку индексных и индексируемых записей. Более того, такая
эскалация может сделать бессмысленной работу пользователей, и
могут быть предприняты специальные административные меры по
отключению пользователей от системы на время таких длительных
операций.
3. В случае если идет массовое удаление индексных записей мы в
итоге должны получить состояние их полного отсутствия. Поэтому нет нужды удалять каждый узел - можно удалить все дерево
целиком.
4. В случае если идет массовая вставка индексных записей то в большинстве случаев мы можем использовать специфические для СУБД
средства, например $SortBegin / $SortEnd в СУБД Cach´e.
5. Мы можем программно включить или отключить опцию журналирования / не журналирования. Чтобы при выполнении операции администратор мог выбрать - использовать журналирование
каждой операции или выключить журналирование, провести перестроение записей и снова включить журналирование, с тем чтобы
после этого выполнить бекап базы - полный или инкрементный. В
первом случае для восстановления базы используется предыдущий
бекап плюс накат журнала, во втором - предыдущий бекап плюс
повтор перестроения индексов либо просто бекап после перестроения индексов. Второй вариант многие администраторы выбирают

298 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
по причине его более быстрой работы - бекап выполняется оптимизированно, поблочно, а перестроение индексных записей - позаписно, поэтому операции с бекапом могут быть более эффективны
чем операции с журналом.
Программно для массового перестроения индексов предпочтительно
иметь специальную утилиту, которую можно вызвать из других утилит
для составления простого средства управления сложной системой.
Поскольку при массовом перестроении индексов индексные структуры не соответствуют данным, выполнение операций перестроения для
проектируемой системы может быть специальной операцией, выполняемой регламентно, в период неактивности пользователей. Иначе для работы системы во время перестроения индексов разработчикам придется
реализовать временные алгоритмы поиска не по индексам, а по самим
данным.
3.22 Операции с древовидными индексами
К операциям с древовидными индексами относятся операции, использующие деревья с индексами и реализующие над ними теоретико - множественные операции. К таким операциям относят логические операции
над множествами: OR (ИЛИ), AND (И), и другие. При этом деревья используются для хранения множеств - операндов и результата операции.
Структурно древовидный индекс удерживает как значения атрибутов,
так и идентификаторы записей. Поэтому в операциях над индексными
деревьями есть две группы операций - как над деревьями значений атрибутов, так и над деревьями, включающими идентификаторы записей.
Для простоты будем оперировать разработанным ранее примером поддержки простых индексов:
CreateRecords()
k ^Index
k ^Data
n i,Figures,Colors,Counts,Figure,Color,Count,id
s Figures="квадрат~круг~отрезок~треугольник"
s Colors="красный~зелёный~синий~белый"
s Counts="2~5~12~8"
f i=1:1:12 d
. s Figure=$p(Figures,"~",$r(4)+1)
. s Color=$p(Colors,"~",$r(4)+1)
. s Count=$p(Counts,"~",$r(4)+1)
. s id=$$InsertRecord(Figure_"~"_Color_"~"_Count)
q

3.22. ОПЕРАЦИИ С ДРЕВОВИДНЫМИ ИНДЕКСАМИ 299
InsertRecord(RecordValues)
n id s id=$i(^Data)
l +^Data(id)
s ^Data(id)=RecordValues
d InsertIndexRecords(id,RecordValues)
l -^Data(id)
q id
DeleteRecord(id)
q:’$d(^Data(id))
l +^Data(id)
n RecordValues s RecordValues=$g(^Data(id))
d DeleteIndexRecords(id,RecordValues)
k ^Data(id)
l -^Data(id)
q
UpdateRecord(id,RecordValues)
q:’$d(^Data(id))
l +^Data(id)
n OldRecordValues s OldRecordValues=$g(^Data(id))
d DeleteIndexRecords(id,OldRecordValues)
s ^Data(id)=RecordValues
d InsertIndexRecords(id,RecordValues)
l -^Data(id)
q
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Figure",id,$p((RecordValues),"~",1))
d InsertIndexRecord("Color",id,$p((RecordValues),"~",2))
q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Figure",id,$p((RecordValues),"~",1))
d DeleteIndexRecord("Color",id,$p((RecordValues),"~",2))
q
InsertIndexRecord(IndexName,id,Value)
s ^Index(IndexName,Value,id)=""
q
DeleteIndexRecord(IndexName,id,Value)
k ^Index(IndexName,Value,id)
q
Рассмотрим операции над деревьями значений атрибутов. К ним можно отнести операции выборки поддерева с заданным значением атрибута,
выбрать поддерево со значениями атрибута меньшими чем заданное, со
значениями большими чем заданное, со значениями между двумя заданными, вычесть поддерево из другого поддерева, и получить число
различных значений атрибутов. Приведем примерные реализации этих
операций и как их использовать в контрольном примере.
Выборка поддерева с заданным значением атрибута.

300 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
EQ(ret,name)
m @ret=@name
q ret
USER>d EQ^TREEOP($NA(a),$na(^Index("Figure","круг")))
Получить поддерево из заданного поддерева кроме указанного значе-
ния.
NE(ret,name,value)
m @ret=@name
k @root@(value)
q
NE2(ret,name,value...)
m @ret=@name
n i f i=1:1:value k:$d(value(i)) @ret@(value(i))
q
USER>d NE^TREEOP($na(res), »
$na(^Index("Figure")),"круг")
USER>d NE2^TREEOP($na(res), »
$na(^Index("Figure")),"круг","квадрат")
Получили всё поддерево с индексом кроме поддерева с указанным
значением. В первом случае стандартный М код, с вычитанием одного значения, во втором - с расширением COS, с вычитанием значений,
заданных списком.
Выборка поддерева со значениями атрибута, меньшими заданного.
LT(ret,name,value)
n i
s i=value f s i=$o(@name@(i),-1) q:i="" m @ret@(i)=@name@(i)
q
USER>d LT^TREEOP($na(r),$na(^Index("Figure")),"отрезок")
Выборка поддерева со значениями атрибутов, большими чем заданное.
GT(ret,name,value)
n i
s i=value f s i=$o(@name@(i)) q:i="" m @ret@(i)=@name@(i)
q
USER>k d GT^TREEOP($na(r),$na(^Index("Figure")),"отрезок")
Выборка поддерева со значениями между двумя заданными.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
