5.11.1. Индексы
Предположим, в нашей базе данных есть таблица persons, содержащая сведения о людях (ФИО и где работают:)
CREATE TABLE persons ( person_id SERIAL UNIQUE, company INTEGER, lname CHAR(40), fname CHAR(30), sname CHAR(30), );
Если мы попытаемся определить, какие люди работают в фирме с уникальным номером 105 (то есть у какх записей в таблице persons поле company равно 105), нам надо будет выполнить запрос
SELECT lname, fname, sname FROM persons WHERE company = 105
При исполнении этого запроса сервер базы данных должен будет просмотрть всю таблицу persons и для каждой записи из этой таблицы проверить условие "company = 105". Если в таблице persons несколько миллионов записей, то такой запрос может потребовать для своей обработки длительное время. Совсем не обязательно иметь таблицу в несколько миллионов записей, чтобы нагрузить сервер работой на несколько минут. Предположим, в таблице companies одна тысяча записей, а в таблице persons всего десять тысяч записей (в каждой фирме, в среднем, работает десять человек). Таблица compenies имеет следующую структуру:
CREATE TABLE companies ( company_id SERIAL UNIQUE, name CHAR(40), address CHAR(40) );
Если мы будем искать всех людей и их рабочие адреса для фирм под названием "АО Рога и Копыта" и "АОЗТ Сделай Сам", то при исполнении запроса
SELECT persons.lname, persons.fname, persons.sname, companies.address FROM persons, companies WHERE company.name IN ("АО Рога и Копыта", "АОЗТ Сделай Сам") AND persons.company = company.company_id
сервер, в строгом соответствии с правилами обработки запросов, должен будет для каждой записи из таблицы persons просмотреть всю таблицу companies. Итого, для исполнения этого запроса, надо будет просмотреть десять миллионов возможных комбинаций (десять тысяч умножить на одну тысячу).
Вернемся к первому примеру. Если бы записи были упорядочены по номеру компании, найти имя человека, работающего в фирме с номером 105 было бы достаточно быстро. Использование алгоритма двоичного поиска (деления пополам) позволяет найти нужную запись в миллионной таблице примерно на двадцатой попытке.15)
Однако в SQL нельзя предугадать, какие будут запросы к данной таблице, Если мы упорядочим таблицу persons по номерам компаний, скорость поиска по фамилии все равно останется низкой, а упорядочить одну и ту же таблицу по двум полям одновременно невозможно. Но в SQL есть способ повысить скорость исполнения определенных запросов. И этот способ основан на индексах. Например, если бы мы хотели повысить скорость поиска записей в таблице persons по полю company, то следовало бы создать индекс по данному поля в данной таблице:
CREATE INDEX pers_comp_index ON persons(company)
В общем случае, оператор создания индекса выглядит так:
CREATE INDEX <имя индекса> ON <имя таблицы> (<имя поля>,<имя поля> ...)
Имя индекса должно быть уникальным среди других имен в базе данных. То есть имя индекса не может совпадать с именем таблицы, с именем другого индекса и т.д. Примеры создания индексов:
CREATE index1 ON persons(lname)
CREATE comp_indx ON items(company, name)
Для того, чтобы четко представлять, как работает индекс и в каких случаях SQL-сервер может использовать индекс для ускорения поиска, рассмотрим один из вариантов внутреннего представления индекса.
Пусть у нас есть таблица persons с полями:
------------T---------T----------T---------¬ ¦ person_id ¦ company ¦ lname ¦ fname ¦ +-----------+---------+----------+---------+ ¦ 1 ¦ 101 ¦ Антонов ¦ Сергей ¦ ¦ 2 ¦ 105 ¦ Шапокляк ¦ Алексей ¦ ¦ 3 ¦ 102 ¦ Антонов ¦ Антон ¦ ¦ 4 ¦ 101 ¦ Бендер ¦ Остап ¦ L-----------+---------+----------+----------
Каждая запись в этой таблице реально где-то размещена. С местом ее размещения может быть однозначно связано какое-то значение. Если для хранения используется файл, как в сервере Informix-SE, то в качестве этого уникального значения может выступать смещение записи от начала файла. Если для хранения используется своя собственная файловая ситема, как, например, в Informix DS, то в качестве внутреннего идентификатора записи может выступать составной ключ (номер диска, номер сектора, смещение от начала сектора). Как выглядит это уникальное для каждой записи значение - абсолютно не важно, это внутреннее дело сервера. Очевидно, что такое значение всегда можно построить. Предположим, что это значение всегда присутствует для любой таблицы и выглядит как псевдополе с именем "rowid" (идентификатор записи). С учетом этого поля записи в данной таблице выглядят следующим образом:
- - - - T-----------T---------T----------T---------¬ ¦ rowid ¦ person_id ¦ company ¦ lname ¦ fname ¦ + - - - +-----------+---------+----------+---------+ ¦ 1003 ¦ 1 ¦ 101 ¦ Антонов ¦ Сергей ¦ ¦ 1023 ¦ 2 ¦ 105 ¦ Шапокляк ¦ Алексей ¦ ¦ 1063 ¦ 3 ¦ 102 ¦ Антонов ¦ Антон ¦ ¦ 1053 ¦ 4 ¦ 101 ¦ Бендер ¦ Остап ¦ L - - - +-----------+---------+----------+----------
При исполнении оператора создания индекса сервер создает набор записей, собственно и составляющих индекс. Каждая запись из индекса состоит из значений той таблицы, по которой построен этот индекс (естественно, хранятся только те поля, которые указаны в данном индексе - эти поля называют проиндексированными) и ссылки на физическое расположение записи в таблице (rowid этой записи). Записи из индекса упорядочены по индексируемым значениям. Например, для индекса pers_comp_indx, построенного по полю company таблицы persons набор составляющих его записей будет выглядеть следующим образом:
-----------T---------¬ ¦ значение ¦ rowid ¦ +----------+---------+ ¦ 101 ¦ 1003 ¦ ¦ 101 ¦ 1053 ¦ ¦ 102 ¦ 1063 ¦ ¦ 105 ¦ 1023 ¦ L----------+----------
Тогда при исполнении запроса
SELECT .... FROM persons WHERE company = 105
сервер определит, что для данного оператора запроса может быть использован индекс. Затем сервер просмотрит список доступных индексов и обнаружит, что существует индекс по полю, использованному в условии данного оператора. Затем сервер просмотрит этот индекс и определит, что данному условию соответствует запись с rowid равным 1023. Просмотр индекса, за счет того, что он упорядочен, займет существенно меньше времени, чем последовательный просмотр всех записей в исходной таблице. Последним действием сервера при исплнении этого запроса будет считыванрие из таблицы persons записи с rowid, равным 1023. По этому значению сервер определит местоположение записи и без просмотра остальных данных сразу прочитает нужную запись.
Очевидно, рассмотренный нами индекс pers_comp_indx сможет быть использован сервером только для тех запросов по таблице persons, где в условии используется поле company. Для следующего запроса, например, данный индекс будет бесполезен:
SELECT company FROM persons WHERE lname = "Бендер"
Если подобные запросы возникают часто, то для того, чтобы убыстрить их обработку, надо создать индекс и по полю lname:
CRATE INDEX second_index ON persons(lname)
Можно создавать индексы и по комбинации полей. Например, если часто проводится поиск по комбинации фамилия-имя, то имеет смысл создать соответствующий индекс:
CREATE INDEX pers_names ON persons(lname, fname)
Заметим, что порядок, в котором указаны поля для составного индекса, существенен. Так, определенный нами индекс по комбинации фамилия-имя будет отличаться от индекса по комбинации имя-фамилия:
CREATE INDEX pers_names2 ON persons(fname, sname)
Кстати, если создан составной индекс по полям (lname, sname), то отпадает необходимость в индексе по полю lname (но не в индексе по полю sname). То есть, если создан индекс по некоторой последовательности полей (a1, a2, ... aN), то он функционально покрывает индекс по последовательности полей (a1, a2, ... aK), если K < N.
У оператора создания индекса помимо базового варианта есть модификации. Более полный синтаксис оператора создания индекса выглядит так:
CREATE [UNIQUE] [CLUSTER] INDEX <имя индекса> ON <имя таблицы> (<имя поля>, <имя поля> ...)
Ключевое слово UNIQUE (вместо него можно использовать слово DISTINCT) означает, что в таблице не допускаются одинаковые наборы значений по совокупности полей, указанным в индексе. Другими словами, это способ следить за уникальностью указанного набора полей. Поэтому такой индекс называется уникальным. Использование уникального индекса функционально идентично заданию уникального ключа в таблице за тем исключением, что для добавления уникального индекса надо иметь привилегии RESOURCE, а для указания уникального ключа в структуре таблицы - быть владельцем таблицы, иметь права администратора или иметь привилегию на модификацию таблицы. Пример:
CREATE UNIQUE INDEX my_index ON persons (person_id, company)
Ключевое слово CLUSTER означает создание так называемого кластерного индекса, который не существует в виде отдельного набора данных (как в предыдущем примере), а реализуется путем физического переупорядочивания записей в таблице. Естественно, для каждой таблицы может существовать только один кластерный индекс. Этот индекс, в отличии от всех других, не требует для своего хранения дополнительного выделения памяти. Пример создания кластерного индексак:
CREATE CLUSTER INDEX my_2nd_index ON persons (lname, fname)
Использование индекса значительно повышает скорость поиска записей при выполнения операторов с логическим условием (то есть для операторов SELECT, DELETE или UPDATE с условием WHERE). При этом, естественно, нужно, чтобы сервер базы данных мог использовать существующие индексы для поиска записей, удовлетворяющих условию в разделе WHERE. С другой стороны, индексы, за исключением кластерных, требуют дополнительных затрат памяти. Кроме того, при выполнении операций, изменяющих содержимое базы данных (INSERT, UPDATE, DELETE), если изменения затрагивают проиндексированные поля, требуется дополнительное время на перестройку индекса. Но так как во многих задачах бОльшая часть обращений к базе данных связана с поиском информации, индексы позволяют значительно увеличить общую производительность информационной системы.
Использование индексов в SQL принципиально отличается от того, как они применяются в индексно-последовательных СУБД типа dBase, Clipper или FoxPro. В случае SQL-сервера, поддержание целостности индекса и его использование - это внутренняя задача самого сервера. У пользователя или программиста, работающего с SQL-сервером, есть только возможность создать (оператор CREATE INDEX) и удалить (оператор DROP INDEX) индекс, а также изменить его структуру (с помощью оператора ALTER INDEX, который здесь не рассматривается). Оператор удаления индекса имеет следующий синтаксис:
DROP INDEX <имя индекса>
Естественно, для удаления индекса надо быть или его владельцем, или иметь права администратора.
- 4.5. Упражнения 67
- Глава 6. Устройство Informix Dynamic Server 165
- Глава 7. Эксплуатация информационных систем 177
- Глава 1 Обзор основных архитектур баз данных
- 1.1. Архитектура на основе разделяемых файлов
- 1.2. Архитектура “Хост-терминал”
- 1.3. Архитектура “Клиент-Сервер”
- 1.4. Архитектура с использованием сервера приложений (трехзвенная архитектура)
- 1.5. Упражнения
- Глава 2 Модели данных
- 2.1. Уровни восприятия данных
- 2.2. Иерархическая модель данных
- 2.3. Сетевая модель данных
- 2.4. Реляционная модель данных
- 2.5. Объектно-реляционная модель данных
- Глава 3 Реализация информационных систем на основе продуктов Informix Software
- 3.1. Обзор продуктов Informix
- 3.2. Варианты построения систем
- Internet/Intranet-конфигурация
- 3.3. Выбор оптимальной конфигурации
- Глава 4 Математические основы реляционных субд
- 4.1. Основные понятия
- 4.2. Ключи
- 4.3. Основные операции над таблицами и их интерпретация
- 4.4. Нормализация
- 4.5. Упражнения
- Глава 5 Язык sql
- 5.1. Типы данных, доступные в sql
- 5.3. Основные sql-операторы для доступа и модификации данных
- 5.4. Управление транзакциями
- 5.5. Продвинутые варианты оператора поиска
- 5.5.1. Поиск по нескольким таблицам
- 5.5.2. Устранение повторения данных в операторе select
- 5.5.3. Вычисления внутри оператора select
- 5.5.4. Логические выражения в условии sql-операторов
- 5.5.5. Слияние двух выборок
- 5.5.6. Сортировка выборки
- 5.5.7. Вставка в таблицу нескольких строк одновременно
- 5.6. Использование sql в языках программирования
- 5.7. Программирование сервера базы данных
- 5.7.1. Динамический sql
- 5.7.3. Хранимые процедуры
- 5.7.4. Триггеры
- 5.8. Ограничители (задание целостности на уровне схемы)
- 5.9. Разграничение в sql прав пользователей
- 5.9.1. Права доступа
- 5.9.2. Права на уровне базы данных
- 5.9.3. Права на таблицы
- 5.9.4. Права на хранимые процедуры
- 5.9.5. Кто и как следит за соблюдением прав
- 5.9.6. Механизм ролей
- 5.9.7. Псевдотаблицы (view)
- 5.9.7. Синонимы
- 5.10. Управление одновременным доступом к данным
- 5.10.1. Что бывает, когда несколько человек одновременно пытаются обновить одни и теже данные
- 5.10.2. Открытие базы данных только для себя
- 5.10.3. Блокирование таблицы
- 5.10.4. Механизм блокирования записей и уровни изоляции
- 5.10.5. Управление ожиданием снятия блокировок
- 5.10.6. Тупиковые ситуации
- 5.11. Повышение скорости обработки запросов.
- 5.11.1. Индексы
- 5.11.2. Буферизация журнала транзакций
- 5.11.3. Блокировка на уровне записей и страниц
- 5.11.4. Эффективное построение запросов
- 5.11.5. Сортировка и поиск по коротким полям. Классификаторы
- 5.12. Объектное расширение sql в Informix ds/Universal Data Option
- 5.12.1. Зачем нужна поддержка объектов в серверах бд?
- 5.12.3. Внедрение объектно-ориентированной технологии
- 5.12.4. Реализация объектного подхода в Informix
- Informix ds/Universal Data Option - объектно-реляционная субд
- 5.12.5. Итак…
- Глава 6. Устройство Informix Dynamic Server
- 6.1. Внутренняя архитектура dsa
- 6.2. Механизм хранения данных
- 6.3. Инсталляция продукта
- 6.4. Запуск и останов сервера
- 6.5. Работа с русским языком
- Глава 7. Эксплуатация информационных систем
- Администрирование серверов баз данных
- 7.2. Обеспечение сохранности данных.
- 7.2.1. Технологии постоянного дублирования
- 7.2.2. Архивация
- 7.2.3. Так как же обеспечить сохранность данных?
- 7.3. Архивирование и восстановление данных
- 7.3.1. Что нужно архивировать
- 7.3.2. Утилиты архивации и восстановления
- 7.3.3. Создание архивов утилитой ontape
- 7.3.4. Восстановление из архивов утилитой ontape
- 7.3.5. Как узнать “когда”?
- 7.3.6. Практические советы
- 7.4. Средства контроля за доступом
- 7.4.1 Как работает аудитинг?
- 7.4.2. Конфигурирование списков протоколируемых событий
- 7.4.3. Задание файлов, запуск и остановка механизма аудитинга
- Анализ протокола
- 7.4.5. Практические советы или Что делать, если вы хотите…
- 7.5. Реагирование на чрезвычайные ситуации
- 7.6. Мониторинг текущего состояния сервера базы данных
- 7.6.1. Кто работает с сервером базы данных
- 7.6.2. Сколько памяти использует сервер бд
- 7.6.3. Сколько свободного места имеется у сервера бд
- 7.7. Достижение требуемой производительности
- 7.7.1. Как узнать, что ждет некоторый запрос
- 7.7.2. Как выяснять причины падения производительности
- 2. Общие принципы предлагаемой технологии
- 3. Как портировать приложение