Блог О пользователеscherbinin

Регистрация

 

Старая песня: оптимизация запросов... - часть 1, пролог


Уж сколько за последние годы написано об оптимизации sql-запросов, а вопросы на sql.ru те же самые -- все же работало, и как-то вдруг перестало?! Heelp!

Начинаю серию статей об оптимизации запросов, где попробую простым язык объяснить, как же работает оптимизатор запросов, и как сделать так, чтобы все было быстро, не гадая при этом на кофейной гуще. Может быть удастся помочь хоть кому-то...

Начнем с простого запроса к одной таблице. Для примера возьмем таблицу заказов из некоторой гипотетической системы учета продаж бананов. Сценарий создания таблицы приведен ниже:

create table dbo.order  (

                        order_id           int                    identity (1, 1)

            ,           customer_id     int

            ,           order_date       datetime

            ,           shipping_date   datetime

            ,           order_no          nvarchar(32)

            ,           order_amt        int

            ,           order_total       money

)


Итак, таблица создана, клиентское приложение написано, пользователи вносят данные и периодически им требуется строить различные отчеты. Например, очень важный отчет – список заказов, которые необходимо доставить определенному клиенту в некоторый день:

customer_id

shipping_date

order_no

order_amt

order_total

1

20080809

З-23

20

100.20

1

20080809

З-23/2

15

200.10

1

20080809

СЗ-24

32

90.50

1

20080809

СЗ-25

11

310.10

Совсем несложно написать запрос,который вернет такой список:

select               customer_id

            ,           shipping_date

            ,           order_no

            ,           order_amt

            ,           order_total

from dbo.order o

where              customer_id    = 1

            and      order_date       ='20080809'


Первое время построение этого отчета занимает 1-2 секунды. Все счастливы, компания процветает, открываются новые офисы по всему миру, база данных растет, и постепенно на построение этого важного отчета уходит все больше и больше времени. С некоторого момента пользователи часами ждут построения отчета, выпивая огромное количества чая, кофе и антидепрессантов, потому что отгрузки срываются, клиенты нервничают, руководство в бешенстве и все винят «бездельников из
IT».


Что же случилось? Почему правильный запрос, совсем недавно выполнявшийся за несколько мгновений, теперь выполняется часами и ставит под угрозу нашу карьеру? Может быть давно пора перейти на
Oracle, DB2 или вообще на MySQL (поговаривают, MySQL действительно быстр)?


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

Назад к динозаврам

Забудем ненадолго про мощные серверы, высокопроизводительные базы данных, и вообще про IT, и подумаем, как был бы организован этот процесс в далекие времена, когда программируемый калькулятор МК был удивительным изобретением нечеловеческого разума.


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

Круглые сутки она заполняет вот такие бланки и складывает их в папки:

 
Каждый день Маша должна предоставлять отделу доставки бланки заказов, которые необходимо отгрузить клиентам. Но, к сожалению, все эти бланки и папки находятся в совершенном беспорядке, потому что Маша много пьет и в результате постоянно роняет и перемешивает бланки:

customer_id

shipping_date

order_no

order_amt

order_total

2

20080605

З-12

67

12.50

3

20080709

З-130

212

100.20

2

20080403

З-23/2

23

200.10

1

20080605

СЗ-21

45

90.50

2

20080708

СЗ-20

16

310.10

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

Когда компания только начала работать и заказов было немного, Маше хватало 8 часов рабочего времени,чтобы найти все заказы на завтра. Однако из-за грамотной работы отделамаркетинга и удивительных вкусовых качеств наших бананов, Маше стало не хватать и целого рабочего дня. Как раз это и произошло с нашим запросом – у сервера нет другого варианта, кроме как последовательно перебирать все записи, отфильтровывая требуемые, и неудивительно, что с ростом объема данных этот процесс будет занимать все больше и больше времени.


Один из вариантов решения проблемы – нанять Маше помощницу, однако с увеличением объема заказов понадобится и вторая, и третья, затем они организуют профсоюз, потребуют повышения зарплаты... В современных терминах это вариант увеличения производительности за счет наращивания мощности аппаратной составляющей.

Другой вариант – увеличить производительность труда самой Маши. Для этого можно воспользоваться несколькими методиками.

Руководство по алгоритмам поиска для Маши Ведро

В нашем случае можно отсортировать все бланки заказов по дате доставки, а Машу отправить на принудительное лечение, чтобы она их больше не путала:

customer_id

shipping_date

order_no

order_amt

order_total

2

20080403

З-23/2

23

200.10

2

20080605

З-12

67

12.50

1

20080605

СЗ-21

45

90.50

2

20080708

СЗ-20

16

310.10

3

20080709

З-130

212

100.20

В терминах баз данных мытолько что создали индекс. Теперь Маша может воспользоваться различными алгоритмами для быстрого поиска необходимых заказов.


Один из таких алгоритмов – двоичный (бинарный) поиск. Работает он следующим образом – допустим, намтребуется найти заказ за 20080403, тогда, множество записей, отсортированное по некоторому ключу (
shipping_date в нашем случае), разбивается на две половины. Средний элемент в нашейтаблице – 200080605 (если количество элементов четное, можно выбрать правый илилевый элемент – это не имеет существенного значения). Дата 20080403 предшестует среднему элементу (200080605) и так как множество записей отсортировано по shipping_date, это значит, что искомый элемент (если он существует), находится в первой половине. Снова повторяем этот процесс – разбиваемся первую половину на две и сравниваемискомое значение со средним элементом. В нашем случае – это 20080605. Повторяем процесс – у нас остался один элемент – 20080403, процесс окончен, заказ найден.


Давайте посчитаем,сколько времени мы сэкономили, используя этот алгоритм поиска. Допустим, что напросмотр одного бланка заказа у Маши уходит 1 минута, тогда на полный просмотр всех заказов у нее ушло бы 5 минут. В нашем случае ей понадобилось просмотреть только три заказа – 3 минуты. Теперь допустим, что заказов не 5, а 10000. Сколько бланков придется просмотреть в худшем случае? Делим 10000 пополам – 5000, 5000 пополам – 2500 и так далее, в результате получается 9 бланков или 9 минут, вместо 10000 минут – преимущество по сравнению с полным просмотром очевидно. Сколько же бланков придется просмотреть в общем случае? Для
n бланков в худшем случае потребуется выполнить log n операций – столько раз можно разделить n на два, прежде чем получится 1.


Двоичный поиск – один их первых алгоритмов, разработанных для оптимизации поиска по большим массивам данных. Однако этот алгоритм имеет один недостаток – количество операций (азначит и время доступа) для разных записей может отличаться. Например, длянахождения заказа от 20080605 понадобится выполнить всего одну операцию просмотра, а для заказа от 20080403 – три. Это может не лучшим образом сказаться на производительности система. В
MS SQL для организации индексов и поиска по ним использует другой алгоритм, чтобы гарантировать одинаковое время доступа ко всем элементам индекса – B+деревья.



Для ответа с цитированием необходимо
выделить часть текста исходной записи

 
О пользователеНик Курков

Отличный пример =)