Страница 8 из 38
Отработка запросов и производительность Как планируется отработка запроса Здесь мне, видимо, придётся изложить хотя бы минимальные отрывки из теории оптимизации запросов, так как наше $#@!%$# программистское образование, как я заметил, обычно даёт непростительно мало информации по этому поводу. Это притом, что и литература, и документация обычно льют на эту тему много воды, но реально полезной информации в них так же маловато. И так, у нас имеется набор таблиц, на которых нужно отрабатывать SQL-запросы. Таблицы сами по себе представляют наборы записей (примерно) одинаковой структуры, которые хранятся на диске в некоторой последовательности. Последовательность эта в естественных условиях обычно бывает близкой к случайной. Тем не менее, выбирать записи обычно в определённом порядке или искать нужную запись по значениям полей. Чтобы облегчить эту работу, во-первых, имеются индексы, а во-вторых, имеется кэш, из которого можно брать часто используемые данные. Далее, SQL-запросы обычно выражают собой некоторую комбинацию реляционных операций. То есть если быть математически точным, то это всё - враньё: SQL не является строго реляционным языком и делает многие вещи так, как удобнее было разработчикам первых версий из interbaseM в начале семидесятых, а не как правильно теоретически или с точки зрения прикладных задач. В частности, допускает дублирование записей, недопонимает ключевые ограничения, и т. д., но это тема для отдельного разговора про SQL вообще, а не про InterBase. Большинство запросов представляет собой ту или иную комбинацию соединения, выборки и проекции. Грубо это можно определить следующим образом: в результате соединения двух таблиц получается таблица, состоящая из конкатенаций записей исходных таблиц, причём выбираются только те конкатенации, где выполняется некоторое условие между полями исходных записей. Как правило, это условие - равенство, а соединение называют эквисоединением или естественным соединением. Естественно, что моё определение грубо, как и сам SQL. В результате выборки одна таблица преобразуется в другую, которая содержит подмножество записей исходной, отвечающих какому-либо условию. Условие на этот раз бывает произвольным, вплоть до вложенных запросов. В результате проекции получается таблица, записи которой содержат лишь часть полей исходной. Кроме этого, часть встречается ещё одна важная для приложений, хотя и не реляционная, операция - сортировка того, что получилось. В целом запрос представляет собой некоторое алгебраическое выражение из вышеперечисленных и других функций, которые в качестве аргументов принимают хранимые таблицы (или результаты других функций) и на выходе дают тоже таблицу. Знаток InterBase или чего-либо покруче может заметить, что источниками записей могут являться представления и хранимые процедуры. Но что такое представление? Всё тот же запрос! То есть выражение. А его можно алгебраически подставить в другое выражение и получить то, что в конечном счёте даст результат запроса. В interbase всё не совсем так просто, появляются кое-какие ограничения, но в целом происходит примерно так. Что же касается процедур, то их InterBase воспринимает, как неупорядоченные, неиндексированные, некэшируемые (мало ли что вычислится при следующем вызове) таблицы. В общем, один из наиболее неудобных источников данных и это следует учитывать. Таким образом, имеется выражение (считаем, что оно уже проверено на правильность) и есть ассортимент операций, с помощью которых его можно реализовать. Естественно, было бы лучше, чтобы результат был получен за один проход по данным. Естественно лучше, чтобы выбирались сразу только те данные, которые нужны. Естественно лучше, чтобы данные сразу выбирались в заданном порядке. Очевидно, что необходимость накапливать промежуточные таблицы целиком резко осложнит ситуацию. И про транзакции нельзя забывать ... В общем, нужно выработать алгоритм, который пройдётся по нужным индексам и хранимым таблицам и отработает запрос с учётом кучи этих и других требований. То есть как можно быстрее и сожрав минимальные ресурсы. Такой алгоритм обычно и понимают под планом запроса. От того, насколько эффективно планирование (то бишь построение такого алгоритма), зависит насколько быстро будет работать СУБД. В простейшем случае можно просто сделать полный перебор всех комбинаций записей участвующих в запросе таблиц, на каждой проверить условия, потом то, что осталось отсортировать и выдать пользователю. Но такой алгоритм полностью убьёт производительность. Ещё один фактор, который учитывают действительно мощные системы, но не InterBase: цели планирования. Можно создать алгоритм, который очень быстро выдаст первую запись, или первые несколько, но остальные будет выдавать сравнительно медленными темпами. А можно тот, который сначала подумает немного дольше, но затем выдаст сразу все записи, потратив в сумме меньше времени. Первый режим рекомендуется для интерактивных систем, а второй - для массовой пакетной обработки. И эти два режима планирования могут давать совершенно различные алгоритмы даже для одного и того же запроса на одних и тех же данных. Вот только InterBase в отсутствии внешних подсказок всегда работает во втором режиме, то есть оптимизируя (насколько умеет) суммарное время. А между тем большинство продуктов Борланда ориентировано именно на интерактивную обработку. В результате - мерзкие паузы при листании записей, если они извлекаются сколь-нибудь сложным запросом. |