Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
курс лекций СБД.doc
Скачиваний:
23
Добавлен:
13.11.2019
Размер:
1.94 Mб
Скачать
  1. Некоторые проблемы администрирования баз данных

    1. Оптимизация запросов

Целью оптимизации является организация эффективной обработки запроса к базе данных. Как правило, существует множество способов отбора требуемых данных и оптимизация запроса предназначена для выбора наилучшего из них, т.е. такого способа, который потребует минимальное количество операций ввода-вывода. В реляционных системах, как правило, организована автоматическая оптимизация – пользователь может не задумываться над способом выражения своих запросов, т.е. над тем, как сформулировать запрос, чтобы система выполнила его с максимально возможной производительностью. Более того, существует реальная возможность, что оптимизатор сформулирует запрос лучше, чем пользователь. Во-первых, автоматический оптимизатор располагает некоторыми статистическими данными о состоянии отношений, благодаря которым он способен более точно оценивать эффективность любой стратегии реализации конкретного запроса. Во-вторых, если с течением времени статистика базы данных значительно изменится (например, база данных будет физически реорганизована), то для реализации запроса может потребоваться совсем иная стратегия, чем до реорганизации; другими словами, может понадобиться повторная оптимизация, или реоптимизация. В реляционных системах процесс реоптимизации – это просто повторная обработка исходного запроса системным оптимизатором. В-третьих, оптимизатор – это программа, поэтому он более "настойчив" по сравнению с человеком. Оптимизатор вполне способен рассматривать буквально сотни различных стратегий реализации данного запроса, в то время как программист вряд ли изучает более трех-четырех стратегий.

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

(((Поставки JOIN (Детали WHERE Имя_Д=’Тестер’))JOIN Поставщики))[Имя_П].

SELECT Поставщики.Имя_П FROM Поставщики, Детали, Поставки WHERE Поставщики.ПN=Поставки.ПN AND Поставки.ДN=Детали.ДN AND Детали.Имя_Д=’Тестер’;

Предположим, что существует порядка 100 строк поставщиков, 50 000 строк поставок и 200 строк деталей. Примерно 1% поставок приходится на деталь «Тестер». Будем полагать, что за одно считывание в оперативную память передается одна запись.

Для обслуживания данного запроса потребуется выполнить последовательность алгебраических операций соединения (Join), выборки (Restrict) и проекции (Project). Большое значение имеет порядок выполнения этих операций. Предположим, что операции будут выполнятьcя в следующем порядке:

  1. Выполнить соединение ПОСТАВЩИКИ и ПОСТАВКИ по атрибуту ПN. В худшем случае (при отсутствии индексов и кластеров) для этого потребуется считать 100 записей о поставщиках и для каждой из них считать все 50 000 записей поставок, чтобы создать возможные соединения, т.е. потребуется произвести 100×50 000 (5 миллионов) считываний. В результате будет получено временное отношение Temp1, состоящее из 50 000 строк поставок, соединенных с соответствующими строками поставщиков.

  2. Выполнить соединение Temp1 с ДЕТАЛИ по атрибуту ДN. Для этого потребуется считать 50 000 строк отношения Temp1 и каждую из них сравнить с 200 строками ДЕТАЛИ. Потребуется произвести 50 000×200 (10 миллионов) считываний.

  3. Результатом этого соединения будет временное отношение Temp2, состоящее из всех данных о поставках и поставщиках отношения Temp1, к которым добавлены данные о деталях. Отношение по-прежнему будет состоять из 50 000 строк, но сами строки будут очень длинными, так как они теперь содержат атрибуты всех трех отношений.

  4. Произвести выборку всех строк отношения Temp2, в которых название детали имеет значение «Тестер». Для этого требуется считать 50000 строк отношения Temp2 и отобрать только те строки, атрибут ДЕТАЛИ которых имеет значение «Тестер». В результате получится временное отношение Temp3, состоящее из 500 строк.

  5. Выполнить проекцию Temp3 по Имя_П, чтобы получить окончательный результат. Для этого требуется считать 500 строк отношения Temp3, выполнить проекцию и возвратить результат.

Итак, суммарное количество считываний при такой последовательности выполнения запроса составит 5 000 000 + 10 000 000 + 50 000 + 500, т.е. 15 050 500 считываний.

Такой же результат можно получить, выполнив первые три шага в обратном порядке:

  1. Выполнить выборку строк отношения ДЕТАЛИ, в которых Имя_Д = ’Тестер’. Для этого требуется считать 200 строк отношения ДЕТАЛИ, в результате получится временное отношение T1, состоящее из 1 строки.

  2. Выполнить соединение T1 с ПОСТАВКИ. Для одной строки T1 считывается 50 000 строк поставок, в результате соединения получается временное отношение T2, состоящее из 500 строк.

  3. Выполнить соединение T2 с ПОСТАВЩИКИ. Для каждой из 500 строк отношения T2 считывается 100 строк клиентов, т.е. производится 50 000 считываний и в результате получается временное отношение T3 из 500 строк, содержащее всю информацию о поставщиках, поставках и детали.

  4. Выполнить проекцию T3 по атрибуту Имя_П и получить требуемый результат. Для этого считывается 500 строк отношения T3 и выполняется проекция.

При выполнении запроса в такой последовательности потребуется 200 + 50 000 + 50 000 +500 = 100 700 считываний, что в 150 раз меньше, чем при использовании первой стратегии отбора. Приведенный пример показывает, что даже простая база данных может функционировать в 150 раз быстрее, используя правильную стратегию обработки запроса.

В нашем примере экономия была достигнута за счет изменения порядка выполнения операций. Используя специальные правила эквивалентности, реляционная система может преобразовывать заданное множество операций в эквивалентное ему множество операций. Каждое правило эквивалентности показывает, что результат, полученный с помощью заданной последовательности операций, можно получить с помощью другой последовательности операций. Такие последовательности операций называются эквивалентными (обозначение – ).

Рассмотрим три отношения А, В и С с атрибутами – А.Х, A.Y, A.Z, B.I, B.J, В.Н, C.L, С.М и C.N соответственно. Для последовательностей реляционных операций существуют следующие правила эквивалентности.

  1. Join (A,B) where A.X=B.IR1

Restrict (R1) on R1.Y

Restrict A on A.YR1

Join (R1,B) where R1.X = B.I

То есть, соединение (Join) двух отношений, за которым следует выборка (Restrict) в результирующем отношении, дает тот же результат, что и первоначальное выполнение выборки в соответствующем отношении (A) и последующее соединение полученного отношения (R1) со вторым отношением (B).

  1. Join (A,B) where A.X=B.IR1

Project (R1) on R1.Y

Project (A) on A.Y, A.XR1

Project (B) on B.IR2

Join (R1.R2) where R1.X = R2.lR3

Project (R3) on R3.Y

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

  1. Restrict (A) on A.XR1

Restrict (R1) on R1.Y

Restrict (A) on A.X and A.Y

То есть, последовательные выборки (Restrict), использующие значения атрибутов одного отношения, можно сгруппировать в одну выборку.

  1. Restrict (A) on A.XR1

Project (R1) on R1.Y

Project (A) on A.X, A.YR1

Restrict (R1) on A.XR2

Project (R2) on R2.Y

Согласно этому правилу, если в отношении за выборкой (Restrict) следует проекция (Project), того же результата можно достичь с помощью проекции, за которой следует выборка. Если атрибут, по которому выполнялась выборка (А.Х), не является частью конечного результата, его нужно включить в первую проекцию, а затем исключить с помощью последней операции проекции.

  1. Join (A,B) where A.X=B.I

Join (B,A) where B.I=A.X

То есть, при соединении двух отношений не имеет значения, в каком порядке они указаны в операторе соединения.

  1. Join (A,B) where A.X=B.IR1

Join (R1 ,С) where R1 .I=C.L

Join (B,C) where B.I=C.LR1

Join (R1,A) where R1.I=A.X

Это правило гласит, что при соединении трех и более отношений соединения можно выполнять в произвольной последовательности.

Правила эквивалентности дают множество путей выполнения заданного запроса. Желательно, чтобы соединения выполнялись как можно позже, так как для выполнения соединений требуется много повторных считываний одного и того же отношения, чтобы найти совпадающие значения атрибутов. Чем меньше соединяемые отношения, тем быстрее удастся выполнить соединение. В рассмотренном примере второй способ эффективнее первого, потому что выборка в отношении ДЕТАЛИ была выполнена раньше, чем выполнялись соединения. Поскольку только одна строка из 200 удовлетворяла условию, удалось сократить соединение ДЕТАЛИ с ПОСТАВКАМИ на 99,5%. Также предпочтительней выполнять проекции перед соединением, поскольку это позволяет уменьшить ширину промежуточных отношений и для их хранения может потребоваться меньше страниц памяти. В нашем примере, вероятно, можно добиться дальнейшего сокращения числа операций ввода-вывода, если выполнить в самом начале проекции по ПОСТАВЩИКИ.ПN, ПОСТАВЩИКИ.Имя_П, ПОСТАВКИ.ПN, ПОСТАВКИ.ДN, ДЕТАЛИ.ДN, ДЕТАЛИ.Имя_Д. Благодаря этому промежуточные отношения станут намного меньше, возможно, они даже полностью разместятся в оперативной памяти, что позволит вовсе избежать операций ввода-вывода.

Чтобы по-настоящему оптимизировать базу данных, нужно обладать статистической информацией о ней. Предположим, имеются три отношения А, В и С, причем А содержит 1000 строк, В – 1000, а С – 10. Каждую из 1000 строк отношения А нужно проверить на возможность соединения с одной из 1000 строк отношения В, а 10 строк отношения С нужно сравнить с 1000 строками отношения В. Используя правило эквивалентности 6, можно выполнить соединение этих трех отношений двумя способами.

  1. Join (A,B)  R1 (10001000 считываний, предположим, что получится 1000 строк) Join (R1,C)  Result (100010 считываний)

  2. Join (В,С)  R1 (100010 считываний, может получиться 10 строк) Join (R1,A)  Result (101000 считываний)

В первом случае понадобится 1 010 000 считываний, во втором – 20 000; экономия составит до 98%. Таким образом, соединения нужно выполнять в таком порядке, который позволит как можно быстрее уменьшить количество строк. Система базы данных сможет это сделать только в том случае, если она обладает статистической информацией о размерах файлов, строк и доле совпадений внешних ключей различных отношений. Тогда можно приблизительно оценить вероятное количество необходимых операций ввода-вывода для различных последовательностей выполнения операций.

При оптимизации запросов СУБД должна также принимать во внимание существование таких объектов базы данных, как индексы, хеш-индексы и кластеры.

Когда два отношения кластеризованы, они уже соединены физически. Любая последовательность операций, в которой участвуют эти отношения, должна начинаться с их соединения (Join), так как оно фактически уже произошло.

На соединения по внешним ключам большое влияние оказывают индексы. Рассмотрим соединение двух отношений А и В, где X – первичный ключ А и внешний ключ В.

Join (A,B) where A.X = В.Х

Если в отношении В существует индекс по атрибуту X, то скорость соединения существенно возрастает: для каждой строки А производится поиск в индексе В.Х и соединяются извлеченные строки. Если индекс В.Х отсутствует, для нахождения в отношении В множества строк, соответствующих каждой определенной строке А, придется полностью его сканировать. Правило эквивалентности 5 гласит, что порядок вхождения отношений в оператор Join не влияет на его результат. Если в отношении А существует индекс по первичному ключу, а в отношении В индекса по внешнему ключу нет, то, вероятно, лучше взять отношение В и для каждой его строки находить соответствующую строку отношения А с помощью индекса А.Х.

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