МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ федеральное государственное автономное образовательное учреждение высшего профессионального образования «Национальный исследовательский ядерный университет «МИФИ» Факультет «КиБ» Кафедра № 36 ПОЯСНИТЕЛЬНАЯ ЗАПИСКА к учебно-исследовательской работе на тему: "Реализация механизма Index-only scans для GiST индексов в СУБД PostgreSQL" Группа К06-361 Студент __________________________ ( (подпись) Руководитель ) (фио) ______________ (_____________ (подпись) ) (фио) Оценка __________________________________________________ Члены комиссии __________________ ( _______________________ ) (подпись) (фио) __________________ ( _______________________ ) (подпись) (фио) __________________ ( _______________________ ) (подпись) (фио) __________________ ( _______________________ ) (подпись) Москва 2014 г. (фио) Пояснительная записка к учебно-исследовательской работе: 43 страницы, 13 рисунков, 6 таблиц, список литературы из 7 наименований. Ключевые слова: индекы в СУБД PostgreSQL, деревья поиска, GIST, стратегии поиска в PostgreSQL, Index-only scans, производительность СУБД, расширяемость PostgreSQL. Для улучшения производительности в современных СУБД применяются разнообразные методы оптимизации поиска. Среди них можно выделить индексирование - создание вспомогательных структур данных для ускорения поиска по критерию запроса. В свою очередь для улучшения работы индексов применяются различные механизмы и алгоритмы – например, частичные, функциональные, многоуровневые индексы и Index-only scans (поиск с использованием только индекса). Хорошая производительность необходима не только стандартным (встроенным), но и новым типам данных. В PostgreSQL для индексирования специальных типов данных используется GIST (обобщенное поисковое дерево). В работе представлены теоретическое обоснование и практические особенности применения механизма Index-only scans для GIST. ОГЛАВЛЕНИЕ ВВЕДЕНИЕ ..................................................................................................................... 4 1. ИНДЕКСЫ ................................................................................................................ 6 1.1. Виды индексов в СУБД PostgreSQL ................................................................ 7 Классы и семейства операторов.......................................................................... 10 1.2. Расширяемость индексов .............................................................................. 11 Специализированные поисковые деревья .......................................................... 11 Поисковые деревья для расширяемых типов данных ....................................... 11 2. GENERALIZED SEARCH TREE (GIST) ................................................................ 12 2.1. Структура GIST ............................................................................................... 14 Search (Поиск) ....................................................................................................... 14 Insert (Вставка) ...................................................................................................... 15 Delete (Удаление).................................................................................................. 16 2.2. GIST. Реализация в PostgreSQL ................................................................... 16 2.3. Сжатие ключей ............................................................................................... 18 Сжатие без потерь ................................................................................................ 19 Сжатие с потерями (“lossy” compression) ............................................................ 20 2.4. Применение GIST ........................................................................................... 21 3. СТРАТЕГИИ ПОИСКА в Postgesql ....................................................................... 25 3.1. Index Scan в PostgreSQL ................................................................................ 30 3.2. Index Only Scan ............................................................................................... 30 4. РЕАЛИЗАЦИЯ INDEX-ONLY SCAN В POSTGRESQL........................................ 31 Visibility Map ........................................................................................................... 32 4.1. Ограничения применения стратегии поиска Index Only Scan ..................... 33 4.2. Реализация Index Only Scan для GIST ......................................................... 34 4.3. Оценка производительности Index Only Scan для GIST .............................. 36 4.4. Перспективы дальнейшей разработки.......................................................... 43 ГЛОССАРИЙ ................................................................................................................ 43 ЗАКЛЮЧЕНИЕ ............................................................................................................. 44 Список литературы ...................................................................................................... 45 3 ВВЕДЕНИЕ Информатизация современного общества приводит к необходимости оперировать всё большими объёмами информации. Задачи, связанные с хранением, доступом и поиском информации становятся сложнее, возрастают требования к надежности и производительности СУБД. В общем случае производительность системы баз данных зависит от многих факторов. Наиболее значимые из них: аппаратные средства и конфигурация сервера, структуры, в которых хранятся данные, количество данных, виды запросов, их количество и степень конкурентности. Одной из основных задач разработчиков СУБД является предоставление разнообразных возможностей по оптимизации всех этих факторов. Затрачиваемые на поиск в БД ресурсы напрямую зависят от количества обращений к внешней памяти. Для оптимизации этого процесса применяются вспомогательные структуры данных – индексы, предназначенные для ускорения выборки данных соответствующих ключам поиска. Эффективность достигается за счет того, что индекс имеет структуру, сбалансированного оптимизированную дерева. Таким под поиск - образом, например, реализация эффективных деревьев поиска имеет определяющее значение для любой системы баз данных. Сегодня, системы баз данных все чаще развертываются для поддержки новых приложений, таких как географические информационные системы (ГИС, GIS), мультимедийные системы, инструменты документов, систем проектирования (САПР, биохимические базы данных и CAD), т.п. библиотеки Возможность 4 реализации новых типов данных, учитывающих специфику конкретной области, позволяет решать многие проблемы, которые неминуемо возникают при развитии проекта, поэтому важнейшим критерием оценки СУБД является её расширяемость. Расширяемостью называется возможность добавления дополнительной функциональности без существенных изменений ядра БД. На практике это означает создание новых типов данных и запросов, надежность и скорость обработки которых не уступают соответствующим параметрам операций со встроенными типами. Для качественной поддержки пользовательских типов данных необходимо реализовать их индексирование. В СУБД PostgreSQL для индексной поддержки расширяемых типов данных используется GIST (обобщенное поисковое дерево). Для дополнительной оптимизации поиска используются механизмы улучшения работы индексов, в частности Index-only scans (поиск с использованием только индекса), позволяющий сократить количество обращений к диску. В работе представлены теоретическое обоснование и практические особенности применения механизма Index-only scans для GIST. Работа структурирована следующим образом. В главе 1 приводится общее описание индексов и их применения в современных системах баз данных. Также в этой главе описана расширяемость индексов. В главе 2 подробно рассматривается структура GIST, его реализация и применение в СУБД PostgreSQL. В 3 главе перечислены различные стратегии поиска, применяемые в PostgreSQL, изложена суть 5 механизма Index-only scans. В главе 4 содержится описание работы по применению Index-only scans для GIST в СУБД PostgreSQL. 1. ИНДЕКСЫ Индекс - объект базы данных, создаваемый с целью повышения производительности поиска данных. Таблицы в базе данных могут иметь большое количество строк, которые хранятся в произвольном порядке, и их поиск по заданному критерию путем последовательного просмотра таблицы строка за строкой может занимать много времени. Индекс формируется из значений одного или нескольких столбцов таблицы и указателей на соответствующие строки таблицы и, таким образом, позволяет искать строки, удовлетворяющие критерию поиска. Индексы позволяют избежать большого числа обращений к таблице, что существенно повышает производительность. Ускорение работы с использованием индексов достигается в первую очередь за счёт того, что индекс имеет структуру, оптимизированную под поиск например, сбалансированного дерева. Рис.1. Индексное дерево в СУБД [1] Физически индекс представляет собой файл на диске. Организацию (структуру) индексного файла и методы работы с ней называют методами доступа (access methods, AM).[2] 6 Индексы в системе баз данных используются не только при поиске, но и для ограничений целостности (создание первичного ключа – primary key), для сортировки, группировки и соединения таблиц. Трудности использования индексов заключаются в возможном снижении скорости обновления, и таблиц в том, что индексы со сложной структурой могут занимать много места на диске. После обновления данных (обновления, удаления, добавления записей) индексы должны перестраиваться. Об эффективности индексирования в конкретном случае можно говорить, произведя оценку выгоды от использования индекса и затрат на его поддержание. 1.1. Виды индексов в СУБД PostgreSQL Индексы связывают ключ и указатель на кортеж таблицы (TID). Все индексы в PostgreSQL – вторичные, они отделены от таблицы и не содержат информации о видимости. [3] 7 Рис 2. Структура индексной страницы в PostgreSQL [5] PostgreSQL предоставляет несколько различных индексных методов доступа (AM) или, иначе говоря, типов индекса: Hash, B-tree, GiST, SP-GiST и GIN. В перечисленных индексах используются различные алгоритмы, наиболее подходящие к разным типам запросов. Hash-индексы предполагают хранение хэша значений, благодаря чему уменьшается размер индексов больших полей. Из-за нелинейности хэш-функций данный индекс нельзя сортировать по значению, что приводит к невозможности использования в сравнениях больше/меньше и «IS NULL». Hash-индексы в PostgreSQL не поддерживают логирование в WAL (журнал опережающей записи) и репликацию. 8 B-tree - стандартный индекс применяемый для таблиц PostgreSQL. Это наиболее часто используемый тип индекса, организованный как сбалансированное дерево, упорядоченных ключей. Подобные индексы поддерживаются практически всеми СУБД, как реляционными, так и нереляционными, и практически для всех типов данных. Но, несмотря на большие возможности для расширяемости типов, данный подход весьма ограничен. Невозможна поддержка запросов, кроме основных операций сравнения, которые предоставляет B-tree (=<, <, =, >, >=). SP-GiST – spatial (пространственный) индекс, древовидная структура, подобная B-tree. Используется для индексации многомерной информации. В PostgreSQL существует несколько реализаций SP-GiST индексов: Radix-tree (Дерево остатков), Quad-tree (Дерево квадрантов), k-d деревья. GiST индекс представляет собой не единственный тип индекса, а особую инфраструктуру, в которой могут быть реализованы различные методы доступа. Для этого нужно определить семь обязательных функций расширяемого AM. На основе этих функций в GIST реализована поддержка различных операций сравнения данных. GIN – обобщённый инверсный индекс, ориентированный на поиск элементов внутри составных значений. GIN-индексы содержат наборы (ключ, список позиций), где список позиций содержит идентификаторы строк, которые содержат ключ.[3] Идентификатор строки может попасть в несколько списков. GIN индексы предназначены для работы с типами, которые могут содержать более одного значения. Например, с помощью 9 индексов этого типа можно найти все массивы, содержащие заданный элемент. Обратный индекс используется полнотекстовым поиском GIST, SP-GIST и GIN индексы поддерживают произвольные операции сравнения (похожесть, пересечение множеств, включение и т.д.). Классы и семейства операторов Каждый из методов доступа может быть специфицирован с помощью отдельного класса операторов (opclass) для каждого столбца в индексе.[6] Класс операторов определяет операторы, которые будут использоваться в индексе для данного столбца. Например, B-tree использовать класс индекс на столбце данных типа int4 будет int4_ops, этот opclass включает функции сравнения для значений типа int4. На практике обычно достаточно класса операторов по умолчанию для типа данных столбца. Основной причиной того, что для некоторых типов данных, есть различные классы операторов, является то, что может быть определена больше, чем одна стратегия поведения индекса. Например, можно сортировать комплексные числа по абсолютной величине или по действительной части. Это можно реализовать, определив два opclass’а для типа данных и затем, выбрав нужный класс, при создании индекса. Класс операторов определяет основной порядок сортировки. В случаях, когда несколько типов данных имеют аналогичное поведение, полезно определить операторы перекрестных типов данных. Классы операторов могут быть сгруппированы, чтобы показать отношения между семантически совместимыми классами. Таким объединением классов операторов является семейство операторов (opfamily). 10 Чтобы создать расширение индекса на колонке нового типа данных, необходимо определить класс операторов для нового этого типа. 1.2. Расширяемость индексов Для качественной поддержки в СУБД пользовательских типов данных необходимо реализовать их индексирование. Это требование породило два крупных исследовательских подхода в расширении технологии поисковых деревьев: Специализированные поисковые деревья Среди наиболее известных из этих деревьев можно назвать пространственные деревья поиска, такие как R-tree. За последние годы было разработано десятки (около 70) [2] различных специализированных методов доступа, однако их реализация в серьезных СУБД связана с большими затратами из-за собственно программирования AM и обеспечения соответствующего уровня надежности, конкурентности, предоставляемых СУБД для обычных AM. [1] Поисковые деревья для расширяемых типов данных В качестве альтернативы созданию новых структур данных было предложено расширить поддерживаемые типы уже известных B-tree и R-tree. Хотя метод и обеспечивает расширяемость данных, он не распространяется на новые типы запросов. Для B-tree и R-tree поддерживаются только стандартные запросы. Это серьёзная проблема для обеспечения гибкости, так как традиционные запросы, связанные с линейным порядком и пространственным положением могут оказаться неприменимы к новым типам данных. 11 В работе [1] было предложено третье направления расширения технологии – введение новой структуры GIST (обобщенное поисковое дерево), сочетающей расширяемость типов данных и запросов. 2. GENERALIZED SEARCH TREE (GIST) GIST(Generalized Search Tree, обобщенное поисковое дерево) – структура данных и API (программный интерфейс), которые могут быть использованы в качестве основы для создания различных деревьев поиска. На основе GIST легко может быть реализован ряд известных индексов, таких как B+-tree, R+-tree и RD-tree. [6] 12 Рис.3. Интерфейс AM (методов доступа) СУБД. [4] GIST является обобщением B+-tree и обеспечивает инфраструктуру поискового дерева с поддержкой параллелизма и восстановления после краха без каких-либо предположений о типе обслуживаемых данных и запросов. [2] GIST может быть использован для любого типа данных, которые могут быть естественным образом иерархически упорядочены. В PostgreSQL GIST представляет собой шаблон для реализации новых АМ для пользовательского типа данных. Код инфраструктуры GIST предоставляет макет индексных страниц, алгоритмы вставки и удаления индексов, и сложные транзакционные детали, такие как блокировка на уровне страниц для поддержки параллельного доступа, и WAL (Write Ahead Log, журнал опережающей записи) для восстановления после сбоев.[4] Это позволяет разработчикам новых индексов сосредоточиться на реализации особенностей нового типа индекса – например, на том каким образом формируется иерархия подмножеств данных, не становясь при этом экспертами во внутреннем устройстве СУБД. GiST интерфейс повышает уровень абстракции, требуя от разработчика AM реализовать только семантику 13 типа данных, который индексируется и те эксплуатационные свойства, которые отличают конкретный AM от других. Учитывая, что разработчики расширений базы данных, как правило, специалисты в предметной области, а не эксперты сервера базы данных, такой подход к расширяемости методов доступа должен привести к гораздо более высокому качеству методов при существенно сниженной стоимости разработки. Структура GIST 2.1. GiST представляет собой сбалансированное по высоте дерево, концевые узлы (листья) которого содержат пары (p, ptr), где p – ключ поиска, а ptr - указатель на соответствующую запись на странице данных. Внутренние узлы содержат пары (p,ptr), где p - это некий предикат (используется как поисковый ключ), выполняющийся для всех наследных узлов, а ptr - указатель на другой узел в дереве. [1] В следующем обзоре рассматриваются общие типы запросов и роль расширения AM GIST в этих алгоритмах. [4] Search (Поиск) Для того, чтобы найти все записи, удовлетворяющие критерию поиска, производится рекурсивный обход всех поддеревьев , для которых предикат родительской записи согласуется с критерием поиска. Интерпретация поискового запроса и его согласования с данными, хранящимися в дереве обрабатывается функцией интерфейса consistent(), которая принимает в качестве аргументов предикат запроса и запись страницы и возвращает TRUE, если запись соответствует поисковому предикату. 14 Insert (Вставка) Получив новую пару (key, RID), требуется найти один лист, в котором будет сформирована новая запись. В отличие от B-tree в GIST могут содержаться записи с перекрытием, так что может существовать больше, чем один лист, допускающий вставку нового ключа. При прохождении пути от корня к листу, в качестве руководящего принципа для выбора указателя на следующего потомка, используется интерфейсная функция penalty(), которая принимает новый ключ и запись страницы в качестве аргументов и возвращает соответствующее числовое значение “стоимости” вставки в данное поддерево. Как правило “стоимость” отражает, насколько должен быть расширен предикат поддерева для размещения нового ключа. На каждом уровне дерева для дальнейшего прохождения выбирается запись с наименьшей “стоимостью”. Если в результате вставки целевой лист переполняется, то он должен быть разделен. Если родительский лист также переполняется, перестроение дерева выполняется рекурсивно. Функция интерфейса picksplit() определяет стратегию разбиения дерева, указывая, какие из записей на странице переходят в правое поддерево родительского узла при выполнении разбиения. Если предикат родительского узла не включает новый ключ, он должен быть расширен таким образом, чтобы сохранялась иерархическая структура вложенности предикатов. Функция union() вычисляет новый предикат, который представляет собой объединение нового ключа с исходным предикатом поддерева. Так же как и разбиение дерева, расширение предикатов выполняется рекурсивно, пока не будет найден узел, предикат которого не требует расширения. 15 Delete (Удаление) Для того чтобы найти лист, содержащий ключ, который необходимо удалить, мы снова просматриваем множество поддеревьев, как при поиске. После того как лист найден, удаляется пара (key, RID) и, если это возможно, перестраивается родительское поддерево. Хотя абстракция GiST предписывает алгоритмы поиска, обновления и удаления записей, разработчик AM имеет полный контроль над удалением страниц, составлением и перестроением предикатов поиска, то есть над характеристиками, которые влияют на производительность индекса.[4] GIST. Реализация в PostgreSQL 2.2. В PostgreSQL GIST представляет собой шаблон реализации новых АМ для пользовательских типов данных. GIST предоставляет разработчикам новых типов данных основные методы для работы с индексными записями. Управлять этими методами можно с помощью интерфейсных функций, которые разработчик должен специфицировать. В настоящей версии [6] GIST содержит 7 обязательных к реализации методов и 1 опциональный. Compress(E): Возвращает запись E = (,ptr), где - сжатое представление исходного ключа p записи E = (p, ptr). [1] Decompress(E): Возвращает запись E = (r,ptr) с таким представлением ключа, что p→r. Компрессия/декомпрессия ключей является потенциально “lossy” (сжатие данных с потерями), так как требование p↔r не обязательно. Всем 16 функциям, кроме Compress(E), запись (ключ) передается только после обработки функцией Decompress(E). Equal(S,T): Проверка равенства записей. Возвращает TRUE, если S = T. Consistent(E,q): проверяет внутренние записи дерева E = (p,ptr) на соответствие предикату запроса q. Возвращает FALSE, если E точно не соответствует запросу q, иначе возвращает TRUE. Стоит отметить, что здесь не требуется точное удовлетворение предиката q. Consistent может вернуть значение TRUE некорректно, но это не повлияет корректность поиска в целом. Такие ошибки могут сказаться в исполнении, вызвав дальнейший поиск в поддеревьях, которые не соответствуют запросу. При проверке концевых узлов дерева (leaf page) поведение этой функции определяется параметром recheck. Если параметр recheck не установлен, необходимо вернуть точный ответ. В противном случае, то есть, если индекс является неточным (“lossy”, с потерями), функция Consistent(p,ptr) возвращает все значения, которые могут соответствовать могут запросу. Union(P): Выполняет объединение ключей. На вход функции подается набор записей (p1,ptr1), … (pn,ptrn). Возвращается некоторый общий ключ r, который содержит все исходные ключи. Это требование может быть осуществлено поиском такого предиката r, для которого выполняется (p1V…Vpn)→r. Penalty(E1, E2): Вычисляет “стоимость” вставки записи E2 в ветвь дерева с вершиной Е1. Используется в алгоритме вставки в индексное дерево. Обычно представляет собой увеличение размера записи от E1 до Union(E1,E2) PickSplit(P): Определяет правило разделения страниц индекса, по которому набор записей P разделяется на 2 набора P1, P2. 17 Distance(E,q): значением, Возвращает переданным расстояние в между запросе. записью Используется и для упорядоченного поиска. Является опциональным методом, то есть при его отсутствии GIST будет функционировать правильно, но без поддержки определенного типа запросов. Все перечисленные методы, должен определить разработчик класса оператора. Стоит учитывать, что функции Consistent, Compress, Union, Penalty должны быть в состоянии обработать любой полученный предикат. Полная реализация работы со всеми вариантами входных данных может оказаться довольно трудной. Но, как правило, в одном дереве может использоваться ограниченный набор предикатов и этим множеством можно ограничить реализацию методов.[4] 2.3. Сжатие ключей При реализации AM есть ряд возможностей в организации сжатия ключей поиска. В простейшей реализации функции Compress и Decompress могут быть тривиальными, то есть возвращать исходную запись, не изменяя ключа. В более сложных реализациях Compress может генерировать действительный, но более компактный предикат, а функция распаковки будет идентичной. Этот метод используется, например, для R-trees, в которых полигон при вставке в дерево преобразуется в bounding box (ограничивающий прямоугольник), который определяет полигон при поиске.[1] Более сложные реализации могут использовать сложные методы, как сжатия, так и распаковки. 18 Далее рассматриваются реализации сжатия с потерями и без потерь на примере существующих классов операторов геометрических типов для AM GIST. Таблица 1. Геометрические типы данных в PostgreSQL.[6] Геометрический тип Размер в байтах Представление типа Описание Point 16 (x,y) Точка в пространстве Line 32 ((x1,y1),(x2,y2)) Прямая Lseg 32 ((x1,y1),(x2,y2)) Отрезок Box 32 ((x1,y1),(x2,y2)) Прямоугольник Path 16+16n ((x1,y1),...) Замкнутый путь (то же, что и полигон) Path 16+16n [(x1,y1),...] Незамкнутый путь Polygon 40+16n ((x1,y1),...) Полигон (то же, что и замкнутый путь) Circle 24 <(x,y),r> Окружность (центр и радиус) Сжатие без потерь Для класса оператора box метод Compress тривиален. Данные, которые хранятся в индексе, имеют то же представление, что и данные в родительской таблице. 19 Сжатие с потерями (“lossy” compression) Для описания сложных объектов часто используют более простые фигуры, аппроксимирующие сложную форму, которые характерны тем, что полностью содержат в себе эти объекты. Такие фигуры называют bounding volume и их гораздо проще использовать для проверки на различные условия, например, на пересечение двух объектов. В качестве bounding volume часто используют сферу, цилиндр или куб (либо окружности и прямоугольники, если речь идёт о двумерных объектах). [2] Для классов операторов polygon и circle метод Compress преобразует фигуры в их ограничительные прямоугольники, поэтому ключ имеет тип box, в то время как тип исходного атрибута polygon/circle. Это представляет одну проблему - сжатие многоугольника к bounding box осуществляется с потерями, то есть теряется часть информации, содержащейся в оригинальных данных. Результатом этого является то, что индекс может вернуться слишком много кортежей – те, которые удовлетворяют ключу поиска, и, возможно ещё дополнительные кортежи, которые ему не соответствуют. Чтобы убедиться, что исполнитель запроса позже отфильтровывает не удовлетворяющие запросу кортежи, мы должны установить флаг recheck, который в PostgreSQL обозначает, что индекс является неточным. В индексах, сжатие с потерями может быть выполнено только в определенном направлении – ключи могут стать более общими, чем они были до этого (например, преобразование полигона в bounding box). Ключи не могут стать более конкретным (например, 20 преобразование многоугольника к максимальному вписанному прямоугольнику), так как в этом случае индекс не будет извлекать все кортежи, которые удовлетворяют предикату запроса. Преимущество методов сжатия с потерями над методами сжатия без потерь состоит в том, что они существенно превосходят первые по степени сжатия. Так, например, размер объекта типа polygon равен 40+16n байт, где n – количество точек полигона, а размер объекта типа box равен 16 байт. Таким образом, для полигонов, которые описывают сложные объекты, сжатие может существенно уменьшить объем хранимой в ключе информации, а соответственно и место занимаемое индексом на диске. Примером подобных объектов могут служить географические данные о границах стран мира. Рис.4. Представление географических границ стран мира с помощью их bounding box. [7] 2.4. Применение GIST С использованием GIST был разработан ряд популярных расширений, которые входят в дистрибутив PostgreSQL. Все модули реализуют типы данных, оптимизированных под конкретную задачу, 21 хранилище, индексный доступ к нему и специализированные запросы. Для того чтобы использовать модуль, необходимо установить модуль, загрузить расширение в определенную БД. Ниже приводится краткий обзор использования этих расширений [2]: tsearch2. Реализация полнотекстового поиска. Отличительной особенностью данного модуля является onlineиндекс и полная интеграция с БД, что дает возможность проводить полнотекстовый поиск с ограничениями по атрибутам. Например, искать по документам, в зависимости от уровня доступа клиента и дате публикации. Tsearch2 поддерживает использование словарей, предоставляет API для их создания. pg_trgm. Поиск похожих строк на основе триграмм. Триграмма - это последовательность из трех соседних букв. Например, слово 'собака' содержит триграммы 'соб', 'оба', 'бак', 'ака'. Используя pg_trgm, можно найти все слова, упорядоченные по похожести слову 'собака'. Этот модуль можно использовать вместе с tsearch2 для полнотекстового поиска с коррекцией ошибок ввода. Ltree. Поддержка данных с древовидной структурой. Стандартный способ работы с иерархическими данными, например, с каталогами, заключается в использовании таблиц связей (parent_id, child_id), что приводит, так или иначе, к необходимости использования рекурсивных запросов. Идея модуля ltree состоит в том, чтобы хранить иерархию связей в 22 специальном типе данных ltree и предоставлять индексную поддержку для основных операций. intarray. Индексная поддержка целочисленных массивов. BLASTgres. Расширение PostgreSQL для поддержки управления данными о биологических последовательностях. btree_gist. Реализация B-tree с использованием GIST. Модуль btree_gist поддерживает практические все основные типы данных, используемые в PostgreSQL, и самостоятельной ценности не имеет, так как встроенный btree гораздо лучше. btree_gist применяется для создания композитных индексов, так как PostgreSQL не поддерживает композитные индексы, созданные с разными AM, например, gist и btree. Типичным примером использования является создание индекса по (ftsindex, ts), где ftsindex - колонка типа tsvector, а ts - timestamp. Такой индекс можно использовать не только для обычного полнотекстового поиска, но и для его ускорения поиска в определенном временном интервале. [2] Индексирование геометрических данных. rtree_gist. . Реализация R-tree с использованием GIST. 23 Рис.5. Пример R-tree. PostGIS. расширение СУБД PostgreSQL предназначенное для хранения в базе географических данных. Индексы в PostGIS представлены в виде R-дерева (R-Tree), реализованного как частный случай обобщенного поискового дерева (GiST). 24 Рис.6. R-tree для населенных пунктов Греции. [2] 3. СТРАТЕГИИ ПОИСКА В POSTGESQL Для оптимизации способа выполнения поисковых запросов, в системах баз данных применяются различные стратегии поиска. 25 В PostgreSQL определены следующие планы запросов: Seq Scan (Sequential Scan) – последовательный поиск. Последовательное полное чтение таблицы, строка за строкой, и фильтрация записей соответствующих ключу поиска. Этот план запроса используется Применение SeqScan для неиндексированных таблиц. эффективно для небольших таблиц, полное прочтение которых оказывается быстрее, чем обращение к индексу. Рис.7. Sequential Scan Index Scan - поиск с использованием индекса. Алгоритм выбора нужного индекса [3]: 1. Поиск номера и типа данных столбца в pg_attribute по имени столбца и таблицы 2. Поиск oid оператора в pg_operator по имени оператора и типам операндов 3. Поиск подходящих индексов по таблице и номеру столбца 4. Поиск oid семейства операторов в pg_opclass по классам операторов подходящих индексов 5. Поиск поддерживаемых индексами операторов в pg_amop по oid семейства операторов и oid оператора 26 6. Индексы, которые поддерживают нужные операторы (найдены в pg_amop) могут быть использованы. Простейший индекс: последовательное прочтение индексного столбца, затем выбор данных из таблицы (обращение только к нужным записям). Рис.8. Index Scan Упорядоченный индекс: проиндексированный столбец отсортирован, что позволяет ускорить упорядоченный поиск (запросы ORDER BY), но не дает преимущества в случайном чтении из таблицы. Рис.9. Index Scan по упорядоченному индексу B-tree индекс: индексированный столбец представлен в виде дерева поиска, что позволяет сократить время, затрачиваемое на выполнение запроса. 27 Рис.10. B-tree индекс GIST индекс: шаблон + API для идексирования пользовательских типов данных. В применении аналогичен B-tree индексу с поддержкой дополнительный операций сравнения. Рис.11. GiST, GIN, SP-GiST индексы Index-only Scan: записи, отобранные при прочтении столбца, направляются напрямую в область вывода, если страницы таблицы помечены видимыми в Visibility map. Время поиска снижается за счет уменьшения количества обращений к таблице. Рис.12. Index-only Scan Bitmap Index Scan: для определения подходящих записей используется Index Scan. Затем, выполняется Bitmap Heap Scan 28 для определения доступности данных. Это связано с версионностью PostgreSQL – БД хранит разные версии строк, чтобы не блокировать читающие процессы. Информация о том, существует ли версия строки в данной транзакции, хранится только в самой таблице, индексы же могут содержать ссылки на невидимые в данной транзакции строки. Рис.13. Bitmap Index Scan Для выбора той или иной стратегии поиска, в PostgreSQL применяется планировщик запросов. Этот механизм определяет оптимальный план запроса, используя данные о возможности применения планов, оценку времени выполнения различных запросов, а также статистику использования таблицы: 1. Проверка доступности различных стратегий поиска. Получение из pg_am информации о методе доступа. Проверка существования всех необходимых функций для осуществления Index Scan. 2. Выбор стратегии поиска На основе данных о планах поиска, типе данных в таблице, типе запроса и статистике, вычисляется оценка “стоимости” использования для всех стратегий. Выбирается оптимальный по скорости план поиска. 29 3. Извлечение данных В соответствии производится с алгоритмом просмотр выбранной страниц и стратегии выборка поиска данных, удовлетворяющих запросу. 3.1. Index Scan в PostgreSQL В индексном сканировании, метод доступа отвечает за выдачу TID для всех кортежей, которые соответствуют ключам сканирования. Метод доступа не участвует в извлечении кортежей из родительской таблицы индекса, в определении видимости кортежа в данной транзакции и в других операциях. Метод доступа может указать, что индекс является lossy (с потерями), или требуется перепроверка, для конкретного запроса. Это означает, что просмотр индекса возвращает все записи, которые соответствуют ключу сканирования, и некоторые дополнительные записи, которые ему не соответствуют. Если опция recheck не указана, то должен быть возвращен именно набор совпадающих записей. В противном случае требуется перепроверка. Для этого механизм сканирования по индексу обращается к таблице для уточнения, действительно ли должен быть выбран данный кортеж. 3.2. Index Only Scan Index-only scans (поиск с использованием только индекса) — механизм, позволяющий выбирать данные из страниц индекса, если они представлены в соответствующем формате. Выигрыш во времени поиска достигается за счет того, что не происходит обращения к heap (страницам таблицы), а данные извлекаются непосредственно из элементов индексного дерева. 30 Index-only scans один из наиболее мощных способов улучшения производительности индексов. Он позволяет избежать обращения к таблице не только в запросах с условием where, но и в select запросах, если все необходимые для ответа данные могут быть получены прямо из индекса. Преимущество в производительности сканирования только по индексу зависит от количества запрашиваемых строк и коэффициента кластеризации индекса. Так, например, при выборе одной строки можно сэкономить только одно обращение к таблице. Эффективность поиска только по индексу будет минимальна, если индекс имеет хороший фактор кластеризации. Этот числовой параметр показывает, сколько раз приходилось читать другой блок таблицы при переходе к следующему значению индекса. 4. РЕАЛИЗАЦИЯ INDEX-ONLY SCAN В POSTGRESQL Поддержка Index-only scans в PostgreSQL реализована начиная с версии 9.2 для методов доступа B-tree и SP-GIST. Но область 31 применения механизма этим не ограничивается – принципиально возможна поддержка любого AM при условии, что данные можно получить из индекса в исходном виде. Если в индексе содержатся только оригинальные данные, а не их неточные представления, к этому индексу может быть применен механизм index-only scans, в котором из индекса возвращаются не только TID кортежей родительской таблицы, но и сами данные. Возможность подержи index-only scan конкретным методом доступа определяется функцией AM amcnreturn(). btreecanreturn() – тривиальная функция, всегда возвращает TRUE. Так как метод доступа btree всегда поддерживает indexonly scans. spgcanreturn() – возвращаемое значение зависит от используемого класса операторов. Если opclass использует метод сжатия с потерями, поддержка index-only scans для него отключена. Visibility Map Каждое отношение в heap содержит Visibility Map (карта видимости), чтобы отслеживать, какие страницы содержат только те кортежи, которые доступны во всех активных транзакциях. Она наряду с основными данными сохраняется в отдельном файле. У индексов нет Visibility Map. Карта видимости хранит один бит на каждую страницу из heap. Установленный бит означает, что все кортежи на странице являются видимыми во всех транзакциях. Это означает, что страница не содержит кортежи, которые должны быть изменены. 32 Если бит в Visibility Map не установлен, он также может быть истинным. Так как биты устанавливаются только при выполнении vacuum, но очищаются при любых операциях по модификации данных на странице. Информация о Visibility Map используется в реализации index-only scans, для удовлетворения поискового запроса только данными из индекса. Index-only scans будет работать только тогда, когда Visibility map показывает, что TID находится на полностью видимой странице, иначе кортеж таблицы надо посетить в любом случае, чтобы проверить MVCC видимость. Эта проверка реализована во внутренней структуре плана Index-only scans, и не требует никаких изменений в методе доступа. 4.1. Ограничения применения стратегии поиска Index Only Scan В версии PostgreSQL-9.3 реализована поддержка index-only scans для SP-GIST индексов. Необходимо уточнить, что некоторые классы операторов SP-GIST могут поддерживать lossy представление данных в индексе. (см. 2.3.) Это выгодно, с точки зрения использования памяти, так как уменьшается количество данных, хранящихся в индексе. Но из-за потерь при сжатии ключей, index-only scans применим не ко всем классам операторов SP-GIST. Аналогичные ограничения существуют для реализации index-only scans для GIST индексов. Некоторые классы операторов расширяющих AM GIST реализуют хранение данных в индексе без потерь, и значит, для них принципиально возможна выборка данных, соответствующих поисковому запросу, только из индекса. 33 Для классов операторов с lossy compression также возможно применение стратегии поиска index-only scan для улучшения производительности запросов типа count(*). [6] Так как для выполнения поиска только по индексу, требуется наиболее актуальная версия Visibility Map, index-only scan может улучшить производительность только тех таблиц, которые обновляются достаточно редко. В противном случае затраты на выполнение VACUUM ANALYZE (очищение файлов баз данных от уже удалённых записей и сбор статистики по данным для оптимизации запросов) и обновление Visibility Map будут превосходить выигрыш в производительности, получаемый при использовании index-only scans. Наилучшим образом index-only scans подходит для запросов, которые характерны для крупных хранилищ данных (относительно большие количества статистических, редко обновляемых данных). Подобные базы данных используются в некоторых GIS, так как обновление картографических данных происходит достаточно редко. 4.2. Реализация Index Only Scan для GIST Для реализации Index-only scans в интерфейс метода доступа GIST добавлена новая интерфейсная функция Fetch(E) в дополнение к существующим. Таким образом, разработчик получает возможность создать метод извлечения данных из индекса без потерь. Этот метод будет применяться при выполнении запросов типа SEARCH для ускорения выборки данных. 34 Функция gistcanreturn() определена следующим образом: если для используемого класса оператора определён метод fetch(), то функция возвращает TRUE, иначе – FALSE. Реализован интерфейсный метод gist_box_fetch() извлечения данных непосредственно из элементов индексного дерева (без обращения к страницам данных) для класса операторов box. Алгоритм выполнения поиска с применением стратегии Index Only Scan для opclass’a box на основе AM GIST: 1. Проверка возможности применения плана index-only scan. Вызов функции gistcanreturn(). Проверка, что для данного opclass’a определен интерфейсный метод fetch(). 2. Последовательный просмотр страниц индекса. 3. Сканирование одной страницы индекса. Проверка всех записей на соответствие предикату поиска. Если кортеж удовлетворяет ключу поиска, формируется элемент типа GISTSearchHeapItem: typedef struct GISTSearchHeapItem { ItemPointerData heapPtr; bool recheck; IndexTuple ftup; } GISTSearchHeapItem; heapPtr – указатель на соответствующую запись в таблице recheck – флаг, указывающий – является ли индекс неточным 35 – ftup поле, содержащее данные извлеченные непосредственно из индексной страницы с помощью функции fetch(). 4. Запись элемента в очередь вывода 4.1. Упорядоченный поиск Элемент добавляется в дерево RB-tree queue с учетом иерархического порядка типа данных 4.2. Неупорядоченный поиск Элемент добавляется в List pageData. 4.3. Оценка производительности Index Only Scan для GIST По результатам тестирования общей функциональности новой для AM GIST стратегии поиска: применение механизма Index Only Scan не нарушает работы других этапов поиска; результаты выборки с использованием индекса совпадают с последовательным сканом и фильтрацией. Для оценки производительности новой стратегии поиска были использованы следующие исходные данные: Таблица box_tbl с 1000000 записей в столбце типа box CREATE TABLE box_tbl as ( select box(p,p) from (select point(random(), random()) p from generate_series(1,1000000)) x); 36 Таблица заполняется случайно сгенерированными данными, так что корреляция между порядком данных в индексе и физическим порядком в heap незначительна. GIST индекс на столбце box таблицы box_tbl CREATE INDEX box_tbl_gist_index ON box_tbl USING gist (box); Различные запросы Рассмотрено 2 типа запросов с использованием пространственного индекса и три различных ограничения на количество данных в выборке. Таблица 2. Тестовые запросы. Запрос №1 select box from box_tbl where box && box(point(0.5,0.5), point(0.6,0.6)) limit 1000 ; Запрос №2 select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1000 ; Запрос №3 select box from box_tbl where box && box(point(0.5,0.5), point(0.6,0.6)); Запрос №4 select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)); Запрос №5 37 select box from box_tbl where box && box(point(0.5,0.5), point(0.6,0.6)) limit 1; Запрос №6 select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1; Таблица 3. Время выполнения тестовых запросов с использованием различных стратегий поиска. Номер Номер Теста запроса План поиска 1 2 3 4 5 Среднее время Bitmap Scan 14.409 15.883 11.639 9.529 10.944 12.4808 Index Only Scan 2.405 2.212 2.015 2.183 2.866 2.3362 Bitmap Scan 6.469 4.956 5.612 9.884 5.789 6.542 Index Only Scan 2.363 1.711 1.502 1.096 1.273 1.589 Bitmap Scan 68.046 44.440 31.627 34.660 34.228 42.6002 Index Only Scan 10.920 10.835 3.038 10.701 10.898 10.4784 Bitmap Scan 19.878 22.687 15.935 18.502 19.059 19.2122 Index Only Scan 8.957 15.459 11.458 11.483 9.863 11.444 Index Scan 0,090 0,068 0,211 0,285 0,078 0,1464 Index Only Scan 0,075 0,086 0,073 0,085 0,941 0,2466 Index Scan 0,040 0,065 0,045 0,047 0,048 0,049 Index Only Scan 0,068 0,068 0,104 0,080 0,121 0,0882 1 2 3 4 5 6 38 Из таблицы 2 можно видеть, что использование стратегии Index Only Scan на определенном типе запросов позволяет сократить время поиска в таблице в 2-6 раз. Этот результат можно считать существенным улучшением производительности работы GIST индекса. Худшим случаем для стратегии Bitmap Index Scan и, соответственно, лучшим для Index Only Scan является такое распределение данных и тип запроса, что каждый кортеж из heap будет возвращен с другой страницы. Из данных анализатора запросов, которые приведены в таблице 4 видно, что при использовании плана Bitmap Index Scan, количество извлеченных из heap блоков приблизительно в 2 раза меньше, чем количество данных в выборке (Heap Blocks: exact=565), то есть каждый второй кортеж возвращен с новой страницы. В то время как использование Index Only Scan позволило получить все требуемые данные непосредственно из индексных страниц (Heap Fetches: 0). При выборке всех данных соответствующих запросу (запросы №3, №4), без ограничения количества, стратегия поиска Index Only Scan также предоставляет ощутимое преимущество в скорости выполнения запроса (см. таблицу 5). В то же время для запросов, где требуется извлечь небольшое количество строк (запросы №5, №6), план Index Only Scan не является оптимальным. Как видно из таблицы 6, он проигрывает в скорости плану Index Scan, так как затраты времени связанные с запуском сканирования и обращением к Visibility Map оказываются больше, чем расходы на одно обращение к heap. 39 Из приведенного выше анализа запросов можно сделать вывод, что применение нового для GIST плана поиска может существенно повысить производительность базы данных, но только при его грамотном использовании с учетом специфики структуры базы данных и типов запросов. Таблица 4. Анализ запроса №2. EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1000 ; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------Limit (cost=48.03..2805.08 rows=1000 width=32) (actual time=3.465..5.398 rows=1000 loops=1) -> Bitmap Heap Scan on box_tbl (cost=48.03..2805.08 rows=1000 width=32) (actual time=3.442..4.593 rows=1000 loops=1) Recheck Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Heap Blocks: exact=565 -> Bitmap Index Scan on box_tbl_gist_index (cost=0.00..47.78 rows=1000 width=0) (actual time=2.425..2.425 rows=9951 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Planning time: 0.086 ms Execution time: 5.789 ms (8 rows) EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1000 ; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------40 Limit (cost=10000000000.28..10000000057.78 rows=1000 width=32) (actual time=0.060..1.091 rows=1000 loops=1) -> Index Only Scan using box_tbl_gist_index on box_tbl (cost=10000000000.28..10000000057.78 rows=1000 width=32) (actual time=0.042..0.661 rows=1000 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Heap Fetches: 0 Planning time: 0.080 ms Execution time: 1.273 ms (6 rows) Таблица 5. Анализ запроса №4. EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) ; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------Bitmap Heap Scan on box_tbl (cost=48.03..2805.08 rows=1000 width=32) (actual time=3.573..15.337 rows=10184 loops=1) Recheck Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Heap Blocks: exact=5551 -> Bitmap Index Scan on box_tbl_gist_index (cost=0.00..47.78 rows=1000 width=0) (actual time=2.449..2.449 rows=10184 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Planning time: 0.039 ms Execution time: 19.059 ms (7 rows) EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) ; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------Index Only Scan using box_tbl_gist_index on box_tbl 41 (cost=10000000000.28..10000000057.78 rows=1000 width=32) (actual time=0.048..7.145 rows=10184 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Heap Fetches: 0 Planning time: 0.075 ms Execution time: 9.863 ms (5 rows) Таблица 6. Анализ запроса №6. EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1 ; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------Limit (cost=0.29..4.09 rows=1 width=32) (actual time=0.026..0.029 rows=1 loops=1) -> Index Scan using box_tbl_gist_index on box_tbl (cost=0.29..3805.78 rows=1000 width=32) (actual time=0.023..0.023 rows=1 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Planning time: 0.000 ms Execution time: 0.040 ms (5 rows) EXPLAIN ANALYZE select box from box_tbl where box <@ box(point(0.5,0.5), point(0.6,0.6)) limit 1 ; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------Limit (cost=10000000000.28..10000000000.34 rows=1 width=32) (actual time=0.048..0.048 rows=1 loops=1) -> Index Only Scan using box_tbl_gist_index on 42 box_tbl (cost=10000000000.28..10000000057.78 rows=1000 width=32) (actual time=0.047..0.047 rows=1 loops=1) Index Cond: (box <@ '(0.6,0.6),(0.5,0.5)'::box) Heap Fetches: 0 Planning time: 0.079 ms Execution time: 0.068 ms 4.4. Перспективы дальнейшей разработки Необходимо решить вопрос поддержки index-only scans для многоколоночных индексов. Это потребует изменения внутреннего интерфейса поддержки методов доступа. В ходе обсуждения с сообществом разработчиков PostgreSQL предстоит выбрать способ расширения интерфейса amcanreturn() AM, в частности изменения функции для возможности index-only scans по отдельным столбцам. ГЛОССАРИЙ Index-only scans – поиск с использованием только индекса GIST (Generalized Search Tree) – обобщенное поисковое дерево СУБД – Система Управления Базами Данных ГИС (GIS) – геоинформационная система AM (Access Methods) – методы доступа TID (Tuple ID) – указатель на кортеж SP-GIST – пространственно-разделенный GIST Opcalss (Operator class) – класс операторов 43 WAL ( (Write Ahead Log) - журнал опережающей записи Lossy compression – сжатие с потерями Bounding volume – ограничивающий объем MVCC (MultiVersion Concurrency Control) – управление конкурентным доступом с помощью многоверсионности ЗАКЛЮЧЕНИЕ В данной учебно-исследовательской работе: 1. Рассмотрены принципы построения и применения индексов на основе обобщенного поискового дерева (GIST). 2. Подробно исследована существующая реализации механизма Index-only scans в PostgreSQL. 3. Проведен анализ ограничений применения Index-only scans для GIST индексов. 4. Реализован интерфейсный непосредственно из метод извлечения данных элементов индексного дерева (без обращения к страницам данных). 5. Осуществлена интеграция метода в существующий алгоритм сканирования. 6. Проведено тестирование механизма Index-only scans для GIST. 7. Проведен анализ результатов работы механизма Index-only scans для GIST. 44 8. Рассмотрены перспективы дальнейшей разработки и применения данного расширения СПИСОК ЛИТЕРАТУРЫ 1. Hellerstein J. M., Naughton J. F., Pfeffer A. Generalized search trees for database systems. – September, 1995. 2. Бартунов О., Сигаев Ф. GiST for Postgres SQL. – 2003. 3. Бартунов О., Коротков А., Сигаев Ф. Вся правда об индексах в PostgreSQL – 2013. 4. Kornacker M. High-performance extensible indexing //VLDB. – 1999. – Т. 99. – С. 699-708. 5. Momjian B. PostgreSQL Internals Through Pictures – 2012. 6. The PostgreSQL Global Development Group. PostgreSQL 9.3.3 Documentation – 2014 7. Berkeley GADM (Global Administrative Areas). 45 46