Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
3.8. БИТМАП ИНДЕКС (BITMAP) 251
от применяемой версии сервера, размера блока базы данных и метода применяемой упаковки битовой карты эту величину можно подобрать экспериментально более оптимальной, или обратиться в техподдержку MUMPS системы за рекомендациями.
В действительности, это тоже еще не все - у реализации битовых функций в Cach´e и MiniM есть техническая особенность - нельзя ис­пользовать нулевое число в качестве позиции в сегменте (третий па­раметр). При этом число ноль получается для идентификаторов, крат­ных размеру сегмента. Чтобы избежать этой проблемы, можно поступить просто - после вычисления правильной позиции в сегменте к ней приба­вить единицу, например, и во всех функциях, использующих сегменты, учитывать эту неправильную единицу. Например:
#define BITSEGMENT 32000 InsertIndexRecord(IndexName,id,Value)
n seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT+1 s $bit(^Index(IndexName,Value,seg),pos)=1 q
DeleteIndexRecord(IndexName,id,Value)
n seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT+1 s $bit(^Index(IndexName,Value,seg),pos)=0 q
К обычным достоинствам битовых индексов относится то, что би­товые операции могут быть оптимизированы и могут выполняться зна­чительно быстрее, чем операции с простыми списками. Другим досто­инством является относительно меньший объем используемой дисковой памяти, чем в случае обычных индексов.
К недостаткам битовых индексов относятся необходимость примене­ния битовых операций к всей битовой карте независимо от того, сколько реально там содержится значимых бит. Поскольку в большинстве слу­чаев процессы обращаются к объектам в относительно небольшом диапа­зоне идентификаторов, вероятность что эти идентификаторы находятся в одном сегменте увеличивается. В случае же уменьшения величины сегмента битовый индекс по структуре и характеристикам приближает­ся к простому обратному списку. В ситуации "размер сегмента равен единице" битовый индекс полностью вырождается в обычный.
К как преимуществам, так и недостаткам битовых индексов, в зави­симости от характера их применения может быть отнесено сочетание их
252 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
реализации с транзакционностью прикладной системы. Для отдельных реализаций битовых индексов транзакционность системы будет негатив­ным фактором, для других она не имеет значения. Более подробной этот вопрос описывается в главе о тарнзакциях.
В любом случае выбор вида индекса оставим за программистом и ру­ководителем проекта. Отметим, что сейчас мы занимаемся только тех­ническими вопросами, на основе которых и можно сделать аргументиро­ванный выбор. В документации и рекомендациях фирм - производителей движков баз данных также можно найти советы по выбору применяемых индексов. Важно понимать, на основе чего они сделаны, каковы харак­теристики движков этих баз данных и для каких характерных операций этот выбор предлагается.

3.9 Битслайс индекс (bitslice)

Резаный битовый индекс представляет собой совокупность структур, отображающих фрагменты значений атрибутов на набор идентификато­ров строк данных. Структурно аналогичен битовому индексу (bitmap), но представляет собой N битовых индексов, работающих для всех зна­чений атрибутов. Индексации подвергаются только числовые атрибуты либо те, которые могут быть приведены к числовому виду. Значение N зависит от типа выбранного атрибута.
В карту входят отдельные карты по каждому биту значения атрибу­та. В примере рассмотрим тип натуральное число. Само число в нашем примере рассматривается как 32-битное без знака. Берутся отдельные биты числа и по этим битам строится 32 отдельных битовых индекса.
При поиске по индексу производится обратная операция - выясняет­ся какие биты в значении атрибута должны быть единичными и какие нулевыми и конструируется соответствующая битовая операция. Опера­ция применяется к всем 32 битовым картам. В результате производится хранение всех значений индексов в фактически ограниченном дисковом пространстве. Причем объем хранения примерно пропорционален коли­честву строк данных и не зависит от кардинальности индексируемых атрибутов.
Приведем пример раскладки значений атрибутов по bit-slice индексу.
Строки данных id Count Битовое представление атрибута
1 17 00000000000000000000000000010001 2 6 00000000000000000000000000000110 3 5 00000000000000000000000000000101
3.9. БИТСЛАЙС ИНДЕКС (BITSLICE) 253
4 8 00000000000000000000000000001000 5 14 00000000000000000000000000001110 6 18 00000000000000000000000000010010
Индексные записи биты битовые карты, младшие разряды слева
0 0101000 1 0010011 2 0011010 3 0000110 4 0100001
В остальных битовых картах нули
Использование bit-slice индексов в силу его структуры может быть оправдано не только для числовых атрибутов, но и для любых, для кото­рых возможно битовое представление фиксированной длины, например, даты, номера телефонов, условные коды в виде нескольких символов. Для использования нечислового атрибута для индексации просто потре­буются две операции:
1. определение максимального количества бит (для корректного до­полнения нулями) для выбранного типа атрибута
2. получение значащей битовой последовательности по значению атри­бута
Как и для битмап (bitmap) индексов, при реальном построении движ­ка, использующего bit-slice индексы, из-за априори большого числа запи­сей следует прибегать к сегментированию битовых карт. Схема сегмен­тирования может быть выбрана, например, такой же как и в предыдущем случае с битмап (bitmap) индексами. Формально говоря, bit-slice индек­сы могут быть совмещены с традиционными bitmap индексами напрямую при выборе совпадающего параметра размера битового сегмента.
К достоинствам bit-slice индексов по сравнению с bitmap индекса­ми относится меньший занимаемый объем при высоко-кардинальных атрибутах. Фактически, из-за своего структурирования bit-slice индек­сы стремятся повторно использовать дисковое пространство и битовые карты. При снижении кардинальности атрибутов bit-slice индексы фак­тически приближаются по соотношению нулевых и единичных битов к битмап индексам или имеют худшие характеристики. На этот факт сле­дует обратить внимание при реализации модулей расширения системы базы данных при реализации битовых операций внешними средствами.
254 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
К недостаткам относится большее время на выполнение разборок по битовым картам и сборок обратно, а также ещё большая по сравнению с битовыми индексами вероятность взаимоблокировок процессов. Также к недостаткам относятся увеличенные в 32 раза (в приведенном примере) затраты на распаковку сжатых битовых карт для получения того же результата, что и при использовании битовых индексов.
То преимущество bit-slice индексов, что они потенциально могут за­нимать меньше места чем битовые, в каких-то случаях может перевесить недостаток, связанный с усложнением выборки. При уменьшении зани­маемого дискового пространства ускорение достигается меньшим коли­чеством дисковых операций и, во-вторых, в силу совмещения в битовых картах информации о различных значениях атрибутов, значительно бо­лее эффективным использованием кеширования.
Проведенный эксперимент с размером, занимаемым индексами на диске в СУБД Cach´e подтверждает теоретические рассуждения:
Число записей Число различных Объем хранения, байт
10000 4 bitmap 116
10000 10000 bitmap 120272
Объем хранения индексируемых данных, 260000
значений атрибута
bit-slice 5396
bit-slice 17452
То есть, при увеличении кардинальности атрибута bit-slice индекс на­много эффективнее по объему хранения, чем bitmap индекс, по объему хранения. Приведенные числа не пропорциональны параметрам исход­ных условий, поскольку приведены не информационная емкость индек­са, а объем, занимаемый структурой данных на диске. Функции $bit в Cach´e оперируют сжатыми битовыми строками. При чтении строка распаковывается, при записи снова сжимается. Поэтому объем хране­ния может варьироваться еще и в зависимости от состояния индекса, насколько хорошо он сжимается реализованным алгоритмом.
Впрочем, пока Cach´e и MiniM не имеют такой встроенной поддержки битовой нарезки, но специализированные движки баз данных или спе­циальный модуль расширения вполне могут выполнять такие битовые операции не прибегая к интерпретируемому режиму. В качестве при­мера применения bit-slice индексов приведем воспроизводимый код для Cach´e и MiniM, построенный на $bit функциях. Код приводится только в учебных целях.
CreateRecords()
k ^Index
3.9. БИТСЛАЙС ИНДЕКС (BITSLICE) 255
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
DeleteIndex(IndexName)
k ^Index(IndexName) q
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Count",id,$p((RecordValues),"~",3)) q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Count",id,$p((RecordValues),"~",3)) q
InsertIndexRecord(IndexName,id,Value)
n i l +^Index(IndexName) f i=0:1:31 d . s $bit(^Index(IndexName,i),id)=(Value\(2**i))#2
256 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
l -^Index(IndexName) q
DeleteIndexRecord(IndexName,id,Value)
n i l +^Index(IndexName) f i=0:1:31 s $bit(^Index(IndexName,i),id)=0 l -^Index(IndexName) q
Здесь приведен вариант не сегментированного индекса, для построе­ния сегментированного нужно дополнить структуру и алгоритм вычис­лением номера сегмента и смещения в сегменте:
#define BITSEGMENT 32000 InsertIndexRecord(IndexName,id,Value)
n i n seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT l +^Index(IndexName) f i=0:1:31 q:’Value d . s $bit(^Index(IndexName,seg,i),pos)=Value#2 . s Value=Value\2 l -^Index(IndexName) q
DeleteIndexRecord(IndexName,id,Value)
n i n seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT l +^Index(IndexName) f i=0:1:31 s $bit(^Index(IndexName,seg,i),pos)=0 l -^Index(IndexName) q
Кстати, можно отметить интересную особенность, что при удалении записи собственно значение атрибута при использовании bit-slice (как и bitmap) индексов, вообще говоря, не требуется, поскольку проставление нулевых значений выполняется на все биты, а их количество определя­ется не значением атрибута, а его типом, тем, сколько бит используется при кодировании атрибута этого типа. Использование значения атрибу­та позволяет сократить число обращений к индексной глобали, если не проставлять несуществующие значения:
DeleteIndexRecord(IndexName,id,Value)
n i n seg,pos
3.9. БИТСЛАЙС ИНДЕКС (BITSLICE) 257
s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT l +^Index(IndexName) f i=0:1:31 q:’Value d . s:Value#2 $bit(^Index(IndexName,seg,i),pos)=0 . s Value=Value\2 l -^Index(IndexName) q
При замене типа с числового на иной, как было описано ранее, нужно использовать не явно заданные выражения получения количества битов, а функции, которые а) определяют число бит для типа и б) получают значения бит для значения атрибута, примерно это может выглядеть так:
DeleteIndexRecord(IndexName,id,Value)
n i,len s len=$$BitCount(IndexName) l +^Index(IndexName) f i=0:1:len s $bit(^Index(IndexName,i),id)=0 l -^Index(IndexName) q
InsertIndexRecord(IndexName,id,Value)
n i,len s len=$$BitCount(IndexName) l +^Index(IndexName) f i=0:1:len d . s $bit(^Index(IndexName,i),id)=$$GetBit(IndexName,Value,i) l -^Index(IndexName) q
BitCount(IndexName)
q:IndexName="Count" 31 q 0
GetBit(IndexName,Value,i)
q:IndexName="Count" (Value\(2**i))#2 q 0
В таком варианте от функций построения битовых карт отделяются две функции, специфичные для используемого типа атрибута. При до­бавлении еще одного типа нужно будет просто изменить соответственно функции BitCount и GetBit. При использовании иных типов чем число­вые можно значительно сократить битовое представление значения атри­бута.
Это пример для односегментных bit-slice индексов. Код приведен без оптимизации, только в учебных целях. Для поддержания разделения по сегментам карт нужно просто скорректировать функции изменения карт:
258 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
#define BITSEGMENT 32000 InsertIndexRecord(IndexName,id,Value)
n i,seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT l +^Index(IndexName) f i=0:1:31 d . s $bit(^Index(IndexName,i,seg),pos)=(Value\(2**i))#2 l -^Index(IndexName) q
DeleteIndexRecord(IndexName,id,Value)
n i,seg,pos s seg=id\$$$BITSEGMENT s pos=id#$$$BITSEGMENT l +^Index(IndexName) f i=0:1:31 s $bit(^Index(IndexName,i,seg),pos)=0 l -^Index(IndexName) q
Наиболее распространенной операцией, где bit-slice индексы дают су­щественное преимущество, является операция суммирования значений атрибутов в столбик.
Продемонстрируем принцип вычисления суммы по bit-slice индексу на примере найденной bit-slice карты:
Строки данных id Count Битовое представление
1 17 010001 2 6 000110 3 5 000101 4 8 001000 5 14 001110 6 18 010010
Колонки справа являются двоичным представлением значений атри­бутов. Математически каждой из позиций соответствует позиция в раз­ложении числа по степеням двойки:
Строки данных id Count Битовое представление
1 17 10001 2 6 00110 3 5 00101 4 8 01000 5 14 01110 6 18 10010
Степени двойки 43210
3.9. БИТСЛАЙС ИНДЕКС (BITSLICE) 259
Каждое из значений атрибутов есть сумма битов умноженных на степень двойки, соответствующей его позиции. Таким образом, полную сумму всех значений можно получить как сумму сумм битов по каж­дой позиции (здесь в виде колонки) умноженных на степень двойки для этой позиции. Для получения суммы битов по каждой колонке исполь­зуется встроенная системная функция $bitcount. Таким образом, для вычисления общей суммы необходимо 5 вызовов функции $bitcount и 5 произведений на степени двойки.
Пусть есть число A, разложимое по основанию 2:
N
X
n=0
an2
n
A =
здесь все коэффициенты anявляются значениями либо 0 либо 1. Тогда сумма чисел A + B + C представима как сумма:
A + B + C =
N
X
an2n+
n=0
N
X
=
(an+ bn+ cn)2
n=0
N
X
n=0
bn2n+
n
N
X
n=0
cn2n=
И для вычисления суммы всех чисел необходимо вычислить суммы битов при каждой из степеней двойки, что и выполняется системной функцией $bitcount.
Битовое системное расширение, функция $bitcount, выполняется на нижнем уровне так, что просто возвращает число единиц или нулей. Выполнение счета проводится на нижнем уровне, в кодах процессора, и очень быстро. При увеличении числа записей выигрыш во времени может достигать сотен раз по сравнению с простым сложением в стол­бик чисел, на получение каждого из которых выполняется обращение к глобали функцией $order.
Одним из наиболее традиционных применений bit-slice индексов для получения ключевого преимущества по скорости работы являются ана­литические системы класса OLAP. В них операции выборки и сумми­рования применяются одновременно к большому числу записей и их атрибутов.
Для битмап и битслайс индексов в примерах используются блоки­ровки, но для операций выполняемых функциями $bit они не требуют­ся, поскольку эти операции проставляют единичный или нулевой бит атомарно. Во время непосредственно выполнения функций $bit никакие
260 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
другие процессы не могут изменить значение переменной в то же самое время. При откате транзакции операции, выполненные функциями $bit, также откатываются атомарно, не затрагивая другие биты.
К интересным особенностям алгоритма поддержки bit-slice индексов можно отнести то, что обе системы, и Cach´e и MiniM, при отсутствии установки бита явным образом используют эту позицию как нулевой бит. Эквивалент нулевого бита используется как при дополнении битовой строки, так и при чтении бита за ее пределами. Поэтому, если разделить операции обновления индексов на три вида
1. При добавлении новой записи.
2. При изменении записи.
3. При удалении записи.
то, несмотря на то, что сама операция простановки битов должна рас­пространяться на все биты получаемые по значению атрибута, при до­бавлении новой записи нет необходимости проставлять нулевые биты, сократив таким образом число дисковых операций, или операций с гло­балом. Даже если произойдет операция отката транзакции, то система возвращает значение бита из явно заданного нулевого значения в такое же значение, эквивалентное нулевому биту. При обновлении или уда­лении записи необходимо менять лишь те биты, которые отличны от имевшихся, неважно было ли значение бита 0 или 1. Такие особенности обновления bit-slice индексов могут заметно сократить время обновле­ния индекса относительно прямолинейного алгоритма полного обновле­ния всех битов.

3.10 Нормирование значений

Для индексирования данных важным моментом является понимание чув­ствительности атрибутов к регистру.
Ключевой операцией для использования индекса является обращение к искомому значению в глобал или локаль, и поиск выполняет непо­средственно СУБД, поскольку значения индексов в MUMPS хранятся сортированно.
В обычных случаях используются значения атрибутов как есть. При этом, для того, чтобы найти необходимую запись в индексе, нужно чтобы она существовала в точности в том же виде. Если мы не предпринимаем