суббота, 21 мая 2016 г.

MUMPS: Составной индекс

Если простой индекс отображает значение одного атрибута на идентификатор или набор идентификаторов строк, то составной индекс отображает совокупность значений двух или более атрибутов на идентификатор или набор идентификаторов строк.

Структурно индексные записи содержат значения не одного, а двух или более атрибутов в определенном порядке. При этом значения атрибутов могут быть как указаны по отдельности в подъиндексах узлов, так и может быть применена функция, получающая по двум или более значениям одно значение, которое не можут быть получено при других значениях атрибутов. Приведем простой пример с одним составным индексом по атрибутам Figure и Color. В первом случае, при раздельном указании значений атрибутов в индексе:
 ^Index("FigureColor",Figure,Color,id)=""
Во втором случае при использовании, например, функции \$listbuild:
 ^Index("FigureColor",$lb(Figure,Color),id)=""
Или при использовании одного значения в виде строки с разделителем:
 ^Index("FigureColor",Figure_"~"_Color,id)=""
Вопрос выбора функции получения составного значения по нескольким оставим программисту. Думаю, что это лучше решать в каждом конкретном случае. В расчет следует принимать не только уникальность её результата для значений аргументов, но и возможность использовать индексную сортировку при выборке данных, а также время вычисления индексирующего значения.

Составные индексы применяются в выборках данных, где задано условие на два атрибута одновременно. Фактически, имея составной индекс по этим атрибутам, мы получаем максимальную скорость выборки.

Кроме указания идентификатора записи в подъиндексе узла индексной записи можно также применить хранение набора идентификаторов в одном значении. Например:
 ^Index("FigureColor",Figure,Color)=idlist
Здесь idlist - список идентификаторов строк. Такая организация индекса позволяет организовать эффективное покрытие. Покрывающим индексом называется индекс, который может быть применен в другом типе выборки. В случае составного индекса например это могут быть узлы двух видов:
 ^Index("FigureColor",Figure)=FigureIdList
 ^Index("FigureColor",Figure,Color)=FigureColorIdList
Например, пусть есть записи
1 шарик    красный   20
2 шарик    зеленый   12
3 квадрат  синий     5
Им будут соответствовать индексные записи:
 ^Index("FigureColor","шарик")=$lb(1,2)
 ^Index("FigureColor","шарик","красный")=$lb(1)
 ^Index("FigureColor","шарик","зеленый")=$lb(2)
 ^Index("FigureColor","квадрат")=$lb(3)
 ^Index("FigureColor","квадрат","синий")=$lb(3)
Второй вариант организации покрывающего индекса есть применение операции не $o() для выборки данных, а операции $q(). При ее использовании в качестве покрывающего индекса может быть использована начальная простая структура вида
 ^Index("FigureColor",Figure,Color,id)=""
В данном случае индекс покрывает две выборки - при указании как обеих атрибутов Figure и Color, так и при указании только атрибута Figure. Приведем пример применения такого покрытия:
 n Data,id,ref
 s Data("FigureColor","шарик","красный",1)=""
 s Data("FigureColor","шарик","зеленый",2)=""
 s Data("FigureColor","квадрат","красный",3)=""
 s Data("FigureColor","квадрат","синий",4)=""
 s Data("FigureColor","шарик","красный",5)=""
 w "Select шарик + красный",!
 s id="" 
 f  s id=$o(Data("FigureColor","шарик","красный",id)) q:id=""  d
 . w id,!
 w "Select шарик",!
 s ref=$na(Data("FigureColor","шарик")) »
         f  s ref=$q(@ref) q:ref=""  d
 . w $qs(ref,4),!
 q
Первая выборка выдает по условию по двум атрибутам, вторая - по одному.

Использование и поддержка покрывающих индексов может быть как очень эффективна по быстродействию и задействованию одного индекса в нескольких задачах, так и может оказаться неэффективна в случае катастрофического роста объема данных, занимаемого самим индексом и фактическим обесцениванием кеширования базы данных. Фактически, составной индекс является прямым конкурентом методу многоиндексной выборки.

В случае если движок базы данных не умеет использовать многоиндексную выборку, то составному индексу альтернатив, конечно, нет. В случае же возможности выбора следует оценить затраты на поддержку составного индекса. При высокой кардинальности атрибутов составной индекс, видимо, может оказаться менее эффективным, чем многоиндексная выборка по совокупным затратам.

Оценку затратности и предпочтительности разных вариантов лучше производить в реальном проекте на реальных объемах данных, сопоставив с используемым аппаратным обеспечением. На общую оценку может повлиять и общая использованная алгоритмика, и объем кеша, и объем данных подлежащих индексированию, и объем получающихся в итоге выборок.

Небольшой пример реализации составного индекса:
ind03 ; составной индекс по Figure м Color
 q
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
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)
 n Figure,Color
 s Figure=$p((RecordValues),"~",1)
 s Color=$p((RecordValues),"~",2)
 l +^Index("FigureColor",Figure,Color,id)
 s ^Index("FigureColor",Figure,Color,id)=""
 l -^Index("FigureColor",Figure,Color,id)
 q
DeleteIndexRecords(id,RecordValues)
 n Figure,Color
 s Figure=$p((RecordValues),"~",1)
 s Color=$p((RecordValues),"~",2)
 l +^Index("FigureColor",Figure,Color,id)
 k ^Index("FigureColor",Figure,Color,id)
 l -^Index("FigureColor",Figure,Color,id)
 q
Здесь поддерживается только один составной индекс.

То, что индекс является составным, одновременно с тем не мешает ему быть простым или кластерным. Эти возможности можно совмещать. В приведенном примере показан простой составной индекс.

Можно также обратить внимание на то, что в индексных записях для большого объема данных оказывается много записей, имеющих дублирующиеся фрагменты. В частности, могут совпадать следующие фрагменты
^Index("FigureColor",Figure,Color)
^Index("FigureColor",Figure)
^Index("FigureColor")


В практически всех MUMPS системах, в отличие от многих простых NoSQL систем, применяется компрессия ключей, и в большинстве случаев такие дублирования фрагментов не приводят к какому-либо существенному росту объема индексов.

Подробнее о книге "MUMPS СУБД"

Комментариев нет:

Отправить комментарий