Планы запросов - это просто!

Администрирование - Оптимизация БД (HighLoad)

Наверное, каждый 1С-ник задавался вопросом "что быстрее, соединение или условие в ГДЕ?" или, например, "сделать вложенный запрос или поставить оператор В()"? В данной статье я не дам вам исчерпывающих инструкций по чтению планов запроса. Но я постараюсь объяснить доходчиво - что это такое и с какой стороны к ним подойти.

Введение

Наверное, каждый 1С-ник задавался вопросом "что быстрее, соединение или условие в ГДЕ?" или, например, "сделать вложенный запрос или поставить оператор В()"?

После чего 1С-ник идет на форум, а там ему говорят - надо смотреть план запроса. Он смотрит, и ничего не понимая, навсегда забрасывает идею оптимизации запросов через планы, продолжая сравнивать варианты простым замером производительности.

В результате, на машине разработчика запрос начинает просто летать, а затем в боевой базе при увеличении количества записей все умирает и начинаются жалобы в стиле "1С тормозит". Знакомая картинка, не правда ли?

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

Более того, я не считаю себя хорошим оптимизатором запросов, поэтому, в статье весьма вероятны фактологические косяки. Ну тут пусть гуру меня поправят в каментах. На то мы тут и сообщество, чтобы помогать друг-другу, верно? 

Если вы уже умеете читать планы запросов, то, наверное, стоит пропустить статью. Тут будет самое простое и с начала начал. Статья ориентирована на тех разработчиков, которые пока еще не выяснили, что это за зверь - план запроса.

Как работает компьютер

А начну я издалека. Дело в том, что компьютеры, к которым мы привыкли, они не такие уж и умные. Вы же наверняка помните первые уроки информатики, или младшие курсы ВУЗа? Помните сортировку массивов пузырьком там, или чтение файла построчно? Так вот, принципиально нового ничего не изобретено в современных реляционных СУБД. 

Если на лабораторках вы считывали строчки из файла, а потом записывали их в другое место, то вы уже примерно представляете, как работает современная СУБД. Да, разумеется, там все намного (совсем намного) сложнее, но - циклы они и в Африке циклы, чтение диска все еще не стало быстрее чтения ОЗУ, а алгоритмы O(N) все еще медленнее алгоритмов O(1) при увеличении N. 

Давайте представим, что к вам, простому 1С-нику пришел человек и говорит: "смотри, дружище, надо написать базу данных. Вот тут файл, в нем строчки какие-нибудь пиши. А потом оттуда читай". Представим, что отказаться вы не можете. Как бы вы решали эту задачу? 

А решали бы вы ее точно так же, как решают ее ребята из Microsoft, Oracle, Postgres и 1С. Вы бы открыли файл средствами вашего языка программирования, прочитали бы оттуда строки и вывели бы их на экран. Никаких принципиально отличных алгоритмов, от тех, что я уже описал - мир не придумал. 

Представьте, что у вас есть 2 файла. В одном записаны контрагенты, а в другом - договоры контрагентов. Как бы вы реализовывали операцию ВНУТРЕННЕЕ СОЕДИНЕНИЕ? Вот прямо в лоб, без каких-либо оптимизаций? 

Контрагенты

ID

Имя

1

Петров

2

Иванов

3

Сидоров

 

Договоры

ID

IDКонтрагента

НомерДоговора

1

1

ИК-1002399

2

1

БМ-0010/УУ

3

2

Счет 09200

 

Давайте сейчас для простоты опустим нюансы открывания файлов и чтения в память. Сосредоточимся на операции соединения. Как бы вы его делали? Я бы делал так:

Для Каждого СтрокаКонтрагент Из Контрагенты Цикл
    Для Каждого СтрокаДоговор Из Договоры Цикл
        Если СтрокаДоговор.IDКонтрагента = СтрокаКонтрагент.ID Тогда
            ВывестиРезультатСоединения(СтрокаКонтрагент, СтрокаДоговор);
        КонецЕсли;
    КонецЦикла;
КонецЦикла;
 

В примере ф-я ВывестиРезультатСоединения просто выведет на экран все колонки из переданных строк. Ее код здесь не существенен. 

Итак, мы видим два вложенных цикла. Внешний по одной таблице, а потом во внутреннем - поиск ключа из внешней простым перебором. А теперь, внезапно, если вы откроете план какого-нибудь запроса с СОЕДИНЕНИЕМ в любой из 1С-ных СУБД, то с довольно высокой вероятностью увидите там конструкцию "Nested Loops". Если перевести это с языка вероятного противника на наш, то получится "Вложенные циклы". То есть, в "плане запроса" СУБД вам объясняет, что вот тут, для "соединения" она применила алгоритм, описанный выше. Этот алгоритм способен написать любой школьник примерно 7-го класса. И мощные боевые СУБД мирового уровня применяют этот алгоритм совершенно спокойно. Ибо в некоторых ситуациях - он лучшее, что есть вообще. 

И вообще, чего это я сразу с "соединения" начал. Давайте предположим, что вам нужно просто найти контрагента по наименованию. Как бы вы решали эту задачу? Вот есть у вас файл с контрагентами. Напишите алгоритм. Я напишу его вот так:

Для Каждого СтрокаКонтрагент Из Контрагенты Цикл
    Если СтрокаКонтрагент.Имя = "Иванов" Тогда
        ВывестиРезультат(СтрокаКонтрагент);
    КонецЕсли;
КонецЦикла;

Нет, ну серьезно, а как еще его можно написать? А никак по сути. Если неизвестно в каком порядке лежат записи в таблице, то придется пересмотреть ее всю, как ни крути. На языке планов запроса это называется Scan. Сканирование. Полный просмотр данных и ничего больше.

Индексы

А как же мы можем ускорить поиск данных в таблице? Ну правда, всё время пересматривать всё - это же зло какое-то.  

Вспомним картотеку в поликлинике или библиотеке. Как там выполняется поиск по фамилии клиента? В деревянных шкафчиках стоят аккуратные карточки с буквами от А до Я. И пациент "Пупкин" находится в шкафчике с карточкой "П". Просматривать подряд все прочие буквы нет необходимости. Если мы отсортируем данные в нашей таблице и будем знать, где у нас (под какими номерами строк) находятся записи на букву "П", то мы существенно приблизимся к быстродействию тетеньки из регистратуры. А это уже лучше, чем полный перебор, не так ли?  

Так вот, слово "Индекс" в данном контексте означает (опять же, в переводе с языка вероятного противника) "Оглавление". Чтобы быстро найти главу в книге, вы идете в оглавление, находите там название главы, потом смотрите номер страницы и идёте сразу на эту страницу. 

Когда базе данных нужно найти запись в таблице, она идет в оглавление, смотрит на название контрагента, смотрит на номер записи, под которой он лежит, и идет в нужную область файла данных сразу за этой записью. 

В виде кода это может выглядеть примерно так:  

Индекс = Новый Соответствие;

// бла-бла

НомерЗаписи = Индекс["Иванов"]
ВывестиРезультат(ТаблицаКонтрагентов[НомерЗаписи]);

Известно, что чудес не бывает, поэтому, память под Соответствие "Индекс", а также поиск в самом соответствии - это небесплатные операции. Но они намного дешевле, чем прямой перебор всех данных. Ах, да, это Соответствие придется постоянно поддерживать в актуальном состоянии при добавлении или изменении основных данных. 

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

Вспомним, как вообще ведется запись в файл. 

fseek(file, position); // переход к нужному адресу
write(file, dataArray, dataLength); // запись dataLength байт из массива dataArray

Если адрес position указывает куда-то в середину файла, и на этом месте есть данные, то они затираются новыми. Если нужно вставить что-то в середину файла (и массива в памяти в том числе) то нужно в явном виде "подвинуть" все, что находится после position, освободив место, а уже потом писать новые данные. Как вы понимаете, "подвижка" данных это опять же циклы и операции ввода/вывода. То есть, не так уж быстро. Ничего в компьютере "само" не происходит. Все по команде. 

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

Ну так вот, мы еще не решили проблему поиска по разным полям. Мы же не можем хранить данные в файле в разном порядке. Одному пользователю по имени, а другому, скажем - по дате. Причем одновременно. Как бы вы решали эту задачу? По-моему, решение очевидно - нужно хранить отдельно данные и отдельно оглавления, отсортированные по нужным полям. Т.е. в базе данные лежат, как придется, но рядышком мы создадим файлик, где записи отсортированы по имени. Это будет индекс по полю "Имя". А еще рядышком будет другой такой же файлик, но отсортированный по полю "Дата". Для экономии места мы будем хранить в индексах не все колонки основной таблицы, а только те, по которым выполнена сортировка (чтобы быстро тут искать, находить номер записи и моментально прыгать к ней, чтоб прочитать остальные данные).

Ребята, которые пишут взрослые СУБД тоже не придумали ничего лучше. Индексы в БД устроены именно так. Все данные из таблицы лежат отсортированными рядышком в отдельной сущности. По сути, индекс, это просто еще одна таблица. И места она занимает пропорционально размеру основной таблицы, что логично. Да, там еще есть разные ухищрения, типа сбалансированных деревьев и всякого такого, но смысл не сильно меняется. 

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

Планирование выполнения запроса

Представьте, что у вас таблица в пять миллионов записей. И есть у нее индекс. Надо быстренько найти запись со словом "Привет". А еще представьте, что у вас такая же таблица, но с тремя записями. И тоже надо найти "Привет". Какой способ поиска выбрать? Открыть файл индекса, пробежаться по нему двоичным поиском, найти нужный номер записи, открыть файл основной таблицы, перейти к записи по ее номеру, прочитать ее? Или запустить цикл от одного до трех, проверив каждую запись на соответствие условию? Современный компьютер циклы от одного до трех выполняет просто ужас, как быстро.  

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

Вот тут уже я бы не стал браться за работу по написанию планировщика, не защитив предварительно диссертацию. Как он там работает и как умудряется делать это вполне сносно - не знаю. Поэтому, ограничимся документацией СУБД. Из нее следует, что на основании статистики планировщик строит несколько возможных вариантов пошагового выполнения запроса, а потом выбирает из них наиболее подходящий. Например, первый попавшийся. Тоже ведь эвристика, разве нет? 

"Что мне сделать сначала" - думает планировщик: "обойти всю таблицу А, отобрав записи по условию, а потом соединить с таблицей Б вложенными циклами, или же найти индексом все подходящие записи таблицы Б, а уже потом пробежаться по таблице А"? Каждый из шагов имеет определенный вес или стоимость. Чем больше стоимость, тем сложнее выполнять. В плане запросов всегда написана стоимость каждого из шагов, которые выполнил движок СУБД, чтобы собрать результаты запроса.

Устройство оператора плана

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

interface IQueryOperator {
    DataRow GetNextRow();
}

для тех кто не понял, что тут написано, поясню. Каждый оператор плана запросов имеет метод "ДайСледующуюЗапись". Движок СУБД дергает оператор за этот метод и при каждом таком дергании добавляет полученную запись к результату запроса. Например, оператор фильтрации записей на входе имеет всю таблицу, а на выходе - только те, которые удовлетворяют условию. Далее, выход этого оператора подается на оператор, например, ПЕРВЫЕ 100, а далее на оператор агрегации (СУММА или КОЛИЧЕСТВО), которые точно так же, внутри инкапсулируют всю обработку, а на выход выдают запись с результатом.

Схематично это выглядит так:

ВсеДанные ->Фильтр(Имя="Петров")->Первые(100)->Аггрегация(КОЛИЧЕСТВО)

Когда вы откроете план запроса, то увидите кубики, соединенные стрелочками. Кубики - это операторы. Стрелочки - направление потоков данных. Данные бегут по стрелочкам от одного оператора к другому, сливаясь в конце в результат запроса.

Каждый оператор имеет некие параметры: количество обработанных записей, стоимость, количество операций ввода/вывода, использование кэшей и прочее и прочее. Все это позволяет судить об эффективности выполнения запроса. Scan таблицы, пробежавший миллион записей и выдавший две на выходе - это не очень хороший план запроса. Но лучше планировщик ничего не нашел. У него не было индекса, чтобы поискать в нем. А может, наврала статистика и сказала, что в таблице три записи, а на самом деле туда успели написать миллион штук, но статистику не обновили. Все это предмет для разбирательства инженером, который изучает запрос.

План запроса - это пошаговый отладчик запроса. Вы пошагово смотрите, что именно, какой алгоритм (в буквальном смысле) применила СУБД, чтобы выдать результат. Примеры самих алгоритмов вы видели - они чрезвычайно сложны, ведь там есть циклы и условия. Даже порой несколько циклов вложены, вот ведь ужас. Важно понимать, какие процессы происходят внутри каждого оператора. Какой алгоритм применялся к массиву записей в процессе выполнения и сколько он работал.

Конкретные операторы, встречающиеся в планах запроса и их внутреннее устройство я планирую рассмотреть в следующей статье. Спасибо за то, что прочитали до конца!

См. также

Комментарии
1. Ildar Gabdrakhmanov (spezc) 299 04.07.17 03:35 Сейчас в теме
В принципе интересно написано)
Manoshkin; +1 Ответить
2. Руслан Миллер (rusmil) 82 04.07.17 08:13 Сейчас в теме
Спасибо, написано очень доступно для понимания
frkbvfnjh; Bazil; +2 Ответить
3. DenisCh Гейтс (DenisCh) 04.07.17 08:29 Сейчас в теме
написано живенько, для новичков вполне подойдёт
theelectric; Silenser; +2 Ответить
4. Николай Васильев (vasilev2015) 249 04.07.17 09:14 Сейчас в теме
я тоже пытался раскрыть тему скорости соединений http://infostart.ru/public/534444/
Но что-то пошло не так )))

Надеюсь, Андрею повезет больше. Удачи.
Evil Beaver; +1 Ответить
5. Иван Иванов (Famza) 80 04.07.17 09:27 Сейчас в теме
опять же, в переводе с языка вероятного противника

Зачет!
theelectric; herfis; DarkUser; pozdeev-artem; корум; artbear; +6 Ответить
6. Антон Стеклов (asved.ru) 33 04.07.17 09:44 Сейчас в теме
Самое главное упущено:

Большая часть операторов плана работает построчно, т.е. для того, чтобы вернуть первую строку, им достаточно получить от нижестоящего оператора одну строку, подходящую по условиям.
Однако некоторым операторам для возврата первой строки необходимы все строки, возвращаемые предыдущим оператором. Это Sort (Что логично, пока мы не получим последнюю строку, мы не можем завершить сортировку), это для одного из наборов строк - Hash join и т.п.

Т.е. когда мы видим оператор Sort в запросе динамического списка - можно начинать ругаться не глядя :)
В последних версиях платформы, кстати, добавили ограничение на использование полей для сортировки списков. Вот именно по этой причине.
sulfur17; brr; +2 Ответить 1
7. Антон Стеклов (asved.ru) 33 04.07.17 09:45 Сейчас в теме
А вот неплохой доклад по теме:
https://www.youtube.com/watch?v=4yHc8-Idf-w
sulfur17; herfis; jif; Evil Beaver; +4 Ответить
8. Андрей Овсянкин (Evil Beaver) 4134 04.07.17 09:49 Сейчас в теме
(6)отчего же упущено? Написано же: на входе одно множество, на выходе - другое. Сортировка сюда подходит.
9. Алексей Новиков (Новиков) 288 04.07.17 10:13 Сейчас в теме
Мне кажется, для 1сника будет интересен также ответ на вопрос: что происходит в субд, когда он жмет F5 (или какую-то другую команду на выполнение) в среде исполнения запроса. По MS SQL такой инфы с нуля я не находил. По PostgreSQL есть шикардосная статья: Путешествие запроса Select через внутренности Постгреса
sulfur17; artbear; Evil Beaver; +3 Ответить
10. Brr (brr) 178 04.07.17 10:50 Сейчас в теме
Спасибо, как раз хотел объяснить дочери (4 класс) реляционные базы, она интересовалась, текст как основа подойдет. "Быстродействие тетеньки из регистратуры" это пять.
rovenko.n; корум; +2 Ответить
11. Илья Васильев (swimdog) 444 04.07.17 10:55 Сейчас в теме
Статью не читал, но имею сказать по существу) "Наверное, каждый 1С-ник задавался вопросом "что быстрее, соединение или условие в ГДЕ?" или, например, "сделать вложенный запрос или поставить оператор В()"?" - лучше чем проверка разных вариантов практически, ничего нет. Один и тот же запрос на разных базах может работать по разному. Например, в одной базе номенклатуры 1000, а в другой 100000. И хорошо работающий запрос из базы с небольшой номенклатурой будет дико тормозить в другой базе. Но если вы не писатель типовых программ или отраслевых решений, то достаточно замеров времени выполнения на вашей конкретной базе. А для понимания работы запросов советую курсы. Они быстрее помогут разобраться новичкам, как писать текст запросов, чем копание в планах запроса. Это совет, но ни в коем случае не отрицательный отзыв на статью!
12. Андрей Овсянкин (Evil Beaver) 4134 04.07.17 11:15 Сейчас в теме
(11)
Статью не читал... хорошо работающий запрос из базы с небольшой номенклатурой будет дико тормозить в другой базе...достаточно замеров времени выполнения...чем копание в планах запроса


Вот это я прямо в рамочку поставлю!
sasha777666; Dementor; JohnyDeath; Бубузяка; DeD MustDie; корум; Sergey.Noskov; Новиков; artbear; +9 Ответить 1
13. Алексей Беспалов (FreeArcher) 52 04.07.17 11:49 Сейчас в теме
Я, даже как знакомый с планами запросов, прочитал с удовольствием и открыл для себя даже новое (я например думал что СУБД гораздо умнее).
Вобщем очень хорошо информация ложится в голову с такими простыми примерами.
Автору спасибо!
14. Артур Аюханов (artbear) 872 04.07.17 12:12 Сейчас в теме
(0) Очередная отличная юморная статья от владельца статуэтки Инфостарт :)
15. kolya_tlt kolya_tlt (kolya_tlt) 10 04.07.17 12:14 Сейчас в теме
Хорошая статья, но кажется не законченной, так как всё равно нет ответа на поставленной вопрос:
"что быстрее, соединение или условие в ГДЕ?" или, например, "сделать вложенный запрос или поставить оператор В()"?
16. Антон Стеклов (asved.ru) 33 04.07.17 13:42 Сейчас в теме
(8) вопрос в том, сколько строк будет прочитано для получения множества, ограниченного TOP.
17. Антон Стеклов (asved.ru) 33 04.07.17 13:43 Сейчас в теме
(15) Ответа на этот вопрос не существует: сам вопрос поставлен некорректно.
Кстати, в случае достаточно простых условий план для этих вариантов будет один и тот же.
Evil Beaver; +1 Ответить
18. Дмитрий (sommid) 04.07.17 14:43 Сейчас в теме
спасибо, ждем-с продолжения
19. Илья Васильев (swimdog) 444 04.07.17 14:49 Сейчас в теме
(12) 1) Идея была в другом. Сначала набрать основу, как правильно писать запросы, а потом лезть внутрь SQL. Набрать основу проще всего: 1) на курсах 2) читая статьи на ИТС или спецресурсах 3) спрашивая на форумах. Поэтому я предложил сначала получить базу на курсах.
2) Я могу предположить, что план запросов в разных базах будет разным, даже в зависимости от наполнения таблиц, статистики и прочего. И план запроса может также ввести в заблуждение, как и замер времени на пустой базе.
3) Для простых случаев не вижу ничего плохого в использовании замера времени. Вот когда замеры вопроса не решают, тогда да. Тогда и КИП лишним не будет.
20. Андрей Овсянкин (Evil Beaver) 4134 04.07.17 15:05 Сейчас в теме
(14) это не статуэтка, а параллелепипед )
21. Илья Васильев (swimdog) 444 04.07.17 15:17 Сейчас в теме
22. Sergey Andreev (starik-2005) 1018 04.07.17 15:18 Сейчас в теме
Дочитал до nested loops, пришла мысль, что автор недопонимает, но осилил дочитать до конца, после чего понял, что нужно дочитывать до конца.
Dementor; DeD MustDie; корум; +3 Ответить 1
23. Андрей Овсянкин (Evil Beaver) 4134 04.07.17 15:52 Сейчас в теме
(22) прочитал комментарий до конца. Два раза, после чего, так и не понял, это была критика или одобрение?
корум; brr; +2 Ответить 1
24. Sergey Andreev (starik-2005) 1018 04.07.17 15:55 Сейчас в теме
(23)
это была критика или одобрение?
Как сказала наш тренер по публичным выступлениям и работе с аудиторией, что если чувства после реплики противоречивые, то однозначно вами манипулируют!
25. Сергей Беликов (HAMMER_59) 30 05.07.17 07:05 Сейчас в теме
Какой кошмар написан про индексы, ведь полно статей на инфостарте, где все правильно расписано.
"Пользователь захочет записать в середину индекса" придётся всё двигать? Какой ужас.
Еще в начале статьи написано, что дальше массивов никуда не ушли. А как же STL, как же списки, очереди?
Индексы хранятся в виде бинарных деревьев: во первых хранятся блоками, где заранее предусмотрено место под вставку, во вторых передвинуть блок в дереве не проблема, проблема - сбалансировать дерево.
26. Андрей Овсянкин (Evil Beaver) 4134 05.07.17 07:31 Сейчас в теме
(25) а вам не пришло в голову, что это необходимое упрощение?

Ну и про STL посмеялся, спасибо. На дрсуге подумайте, как реализованы ваши списки и очереди.
sasha777666; artbear; starik-2005; +3 Ответить 2
27. Александр Дмитриев (МимохожийОднако) 116 05.07.17 08:53 Сейчас в теме
Начало статьи было хорошее. Планируется продолжение? Осталось ощущение недосказанного, оборванного вдруг...
29. Сергей Беликов (HAMMER_59) 30 05.07.17 12:32 Сейчас в теме
(26) Может Вам лучше всё-таки узнать как утроены деревья, а не смеяться на тем чего не знаете? Я Вам конкретно указал, что не существует выдуманной Вами проблемы вставки элемента в середину дерева. Списки, деревья, каталоги, очереди - это уже совсем не массивы, про которые Вам рассказали в школе. Стандартная библиотека шаблонов, довольно, кардинально изменила подход в работе с данными.

Необходимое упрощение? Т.е. эта такая мелочь, что именно с помощью индексов достигается масштабируемость?

Лично я не вижу смысла заглядывать в план построения запроса, не беспокоитесь заглядывал, просто не вижу в этом никакого смысла, когда задачу можно решить куда более простым путём.

Консоль запросов - умеет замерять затраты на отдельные части запроса. Видим, в каком месте проблема, а проблема всегда в одном - не используются индексы. И разбираемся почему не используются, как правило, запрос написан так, что SQL или другой СУБД слишком сложно разобрать запрос. Проблема решается, как правило, дроблением огромного запроса, на несколько мелких.

Также нужно понимать, какие индексы строит 1С, что местами не очень очевидно, например в случае с периодическими регистрами сведений, мало того, концепция построения индекса для периодического регистра сведений различная, для различный версий программных файлов.

Подытожим:
Какой смысл заглядывать в план построения запроса? Чтобы найти узкое место? Так его можно найти намного проще.
В статье написано, что вот мол на маленькой базе всё нормально, а при большом количестве данных начинает всё тормозить. Т.е. заранее сможете по плану построения запроса сделать прогнозы? Так это враньё, т.к. лишний раз СУБД не будет использовать индексные файлы, и план построения запроса будет зависеть от состояния БД, мало того ещё и от статистики будет зависеть.
30. Сергей Беликов (HAMMER_59) 30 05.07.17 12:56 Сейчас в теме
(26)
Да собственно в чем проблема, долго пример списка привести, все остальные объекты (деревья, очереди, словари и т.д.) подобны списку.

Для списка мы храним ссылку на первый элемент, в случае пустого списка храним NULL.

Каждый элемент, хранит
- Ссылку на данные.
- Ссылку на следующий элемент списка. Последний элемент хранит NULL.

В чём проблема вставить элемент в середину списка? Сколько это займёт времени?
31. Sergey Andreev (starik-2005) 1018 05.07.17 13:19 Сейчас в теме
(30)
В чём проблема вставить элемент в середину списка?
Всегда интересовался, как в таком списке найти некий элемент X. Приведите пример его поиска, отличного от scan.
Evil Beaver; sasha777666; +2 Ответить 1
32. A M (DarkUser) 05.07.17 13:51 Сейчас в теме
33. Сергей Беликов (HAMMER_59) 30 05.07.17 14:28 Сейчас в теме
(31)
На Си со времен института не писал, могу ошибаться в названии объектов.
Создаём MultiSet, в этот объект добавляем ключ (реквизит по которому будем осуществлять поиск), и ссылку на элемент списка. Собственно индексы подобным образом и работают.
А поиск по MultiSet очень быстро отработает.

Говоря языком 1С, то же самое что добавить индекс к таблице значений.
34. Sergey Andreev (starik-2005) 1018 05.07.17 15:59 Сейчас в теме
(33)
А поиск по MultiSet очень быстро отработает.
Тут есть одна проблема: когда мы добавляем индекс, то нам нужно при вставке каждый раз этот индекс расщеплять на часть до и часть после. Вот есть у Вас тот мультисет - это просто список с хештаблицей к элементам, полагаю. Вот добавили вы его - и вроде все хорошо, добавился элемент. Но дальше вам нужно этот индекс сохранить - это запись. Допустим у Вас есть какая-то недозанятая страница и Вы просто в нее пишите. Если вставлять подряд кучу данных, то однажды незанятые страницы закончатся и нужно будет какую-то страницу расщеплять на две, в итоге будут две наполовину пустые страницы. Но при этом в саму таблицу Вы вставляете элементы в конец и не особо паритесь с поиском, ибо есть индекс. Но когда есть кластерный индекс, то при каждой вставке будет искаться свободная страница для самой таблицы, и если ее нет - она будет расщепляться, ибо кластерный индекс задает упорядочивание самой таблицы.

Дальше Вам надо что-то по кластерному индексу найти. В итоге это уже или скан, что нехорошо, или сик. Второе - это поиск в кластерном индексе (читай - в таблице) по полям этой таблицы, которые уже лежат в неком списке со ссылкой на элементы, которые больше, и на элементы, которые меньше -B-TREE. Так вот и индекс в виде такого дерева хранится, а не в виде мультисета (если, конечно, это обычный индекс, который юзается в 1С, а не gist/hash-индексы, которые в 1С не юзаются).
JohnyDeath; Evil Beaver; sasha777666; +3 Ответить 1
35. Андрей Овсянкин (Evil Beaver) 4134 05.07.17 18:03 Сейчас в теме
(29)
выдуманной Вами проблемы вставки элемента в середину дерева


Я такого не писал. Я писал о невыдуманной мной проблеме вставки в середину файла. И написал, как она решается. Кроме того, сбалансированные деревья я также упомянул.

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

Поверьте, довольно большой процент "программистов 1С" на собеседованиях не знает даже этого. У них есть мантра "включить индексирование", а что это и зачем, и как вообще выглядит - не знают.
36. Андрей Овсянкин (Evil Beaver) 4134 05.07.17 18:06 Сейчас в теме
(30) проблема вставки в середину списка заключается в поиске середины списка. Мне неизвестно иного способа, кроме сканирования списка.
37. Сергей Беликов (HAMMER_59) 30 06.07.17 07:14 Сейчас в теме
(34)
Вот есть у Вас тот мультисет - это просто список с хештаблицей к элементам, полагаю.

Неверно предполагаете, мультисет - это дерево, а дерево это есть частный случай списка.

Структура списка:
- Ссылка на данные
- Следующий элемент

Структура дерева :
- Ссылка на данные
- Левая ветвь
- Правая ветвь

Поэтому я никак не могу вас понять почему в список быстро вставлять, а в дерево (в индекс) долго. Дерево - это частный случай списка.

Не вижу никакой проблемы вставлять элементы в дерево. Вы каждый упоминаете кластерный индекс, но его используют далеко не везде и не всегда.
А я пишу про обычное дерево (обычный индекс), и я не вижу никаких проблем в добавление нового элемента в дерево. Да, каждый раз перед записью будет происходить поиск нужного узла дерева, но это не так долго, для глубины дерева в 20 элементов это уже 1 млн значений, и чем больше глубина тем больше разница, т.е. при глубине дерева 30 элементов это уже 1 млрд. значений.
Да, при количестве элементов в 1 млн, при каждой записи будет осуществлен перебор до 20 элементов (может и с первого раза повезет), но 20 элементов перебрать всяко лучше чем 1 млн.
38. Андрей Овсянкин (Evil Beaver) 4134 06.07.17 09:36 Сейчас в теме
(37) я не совсем понимаю, с чем конкретно вы пытаетесь спорить, и в чем нас убеждаете?
39. Сергей Беликов (HAMMER_59) 30 06.07.17 10:57 Сейчас в теме
(38) Так Вы читайте, а не только пишите и станет всё понятно.
Не ленивый, повторю ещё раз.
Почему Вы решили, что запись в индекс происходит долго? Я не вижу никаких оснований, т.к. индекс не что иное как дерево, а дерево это частный случай списка.

Ну и так по мелочи:
Я так и не понял зачем лезть в план запроса.
Во-первых план запроса не является статичным, и для одного и того же запроса меняется в зависимости от ситуации. Я думаю, с этим встречался практически каждый когда один и тот же запрос, в одной и той де базе отрабатывает очень по разному.
Если нужно найти узкое место, зачем лезть в план построения запроса? Можно и без этого получить данные какая часть запроса отрабатывает медленно, и сделать соответствующие выводы.
40. Сергей Беликов (HAMMER_59) 30 06.07.17 11:58 Сейчас в теме
(38)
70% статьи написано, про то, что индексы значительно ускоряют поиск.
Я не согласен с самой подачей материала. Для меня индекс - это метод дихотомии, который я могу объяснить очень просто.
Допустим, у Вас нет операции извлечения квадратного корня, напишите функцию нахождения значения.
Допустим, с точностью до 0,001 знака, находим квадратный корень из 150. А дальше все просто если мы используем перебор, нам придется сделать:
12 247 вычислений, прежде чем мы найдем верное значение.
А для метода дихотомии до нужного значения мы дойдем на 19 шаг.

И прямо тут же можно добавить, а вот если бы искали с точность до 1 (т.е. значений было бы всего 150 а не 150 000), получилось бы одинаковое количество вычислений - по 12, и ничего бы мы не выиграли от использования индекса, а только потеряли время на построение индекса.

Оставшаяся часть статьи совсем не понятно про что. СУБД строит план выполнения запроса по неким волшебным алгоритмам, которые нам не подвластны и не понятны.
Я бы сформулировал по-другому. Если вы написали запрос так, что там сам чёрт ногу сломит, не рассчитывайте, что с ним разберётся СУБД, поэтому будьте проще, учитесь писать простые и понятные запросы, если запрос сложный - упрощайте, используйте вложенные таблицы.
41. Андрей Овсянкин (Evil Beaver) 4134 06.07.17 13:39 Сейчас в теме
(39)
Можно и без этого получить данные какая часть запроса отрабатывает медленно, и сделать соответствующие выводы.


Поделитесь, пожалуйста, как можно ДОСТОВЕРНО получить сведения о том какая часть запроса отрабатывает медленно, не заглядывая в план запроса. Эмпирически, основываясь на собственном опыте - да можно. Но эта информация будет носить вероятностный характер. План запроса ДОСТОВЕРНО показывает - какая часть запроса работает медленно здесь и сейчас. Вот и вся причина туда заглядывать.
42. Сергей Беликов (HAMMER_59) 30 06.07.17 14:15 Сейчас в теме
(41)
Поделитесь, пожалуйста, как можно ДОСТОВЕРНО получить сведения о том какая часть запроса отрабатывает медленно, не заглядывая в план запроса.

Уже отвечал на этот вопрос - консоль запросов позволяет получить исчерпывающие ответы.
Цитата текста its.1c.ru:

При разработке запросов в конфигураторе, как правило, требуется проводить отладку запроса на реальных данных. Данный инструмент позволяет вести разработку запроса (или пакета запросов) параллельно с просмотром результата. При работе с инструментом в толстом клиенте можно воспользоваться конструктором запросов, как и при работе в конфигураторе. Возможности по анализу результата запроса включают:

вывод данных временных таблиц;
замер времени выполнения запроса и числа строк;
подсветку указанных ячеек в результате запроса;
интерактивное сравнение двух результатов запроса (только в толстом клиенте);
вывод результата запроса в новом окне;
вывод плана выполнения запроса, а также SQL-текст запроса, сформированного в СУБД. Для СУБД Microsoft SQL Server план выполнения выводится в виде дерева, а для остальных СУБД – в текстовом формате технологического журнала. Для упрощения анализа запросов также предусмотрено два режима отображения текстов запросов: с именами таблиц и колонок СУБД или с именами объектов метаданных и реквизитов конфигурации (только в обработке для "1С:Предприятие" версии 8.3).
43. Артур Аюханов (artbear) 872 07.07.17 10:00 Сейчас в теме
(42) Сами себе противоречите :(
(39) - Я так и не понял зачем лезть в план запроса.
Во-первых план запроса не является статичным, и для одного и того же запроса меняется в зависимости от ситуации. Я думаю, с этим встречался практически каждый когда один и тот же запрос, в одной и той де базе отрабатывает очень по разному.
Если нужно найти узкое место, зачем лезть в план построения запроса? Можно и без этого получить данные какая часть запроса отрабатывает медленно, и сделать соответствующие выводы.


(42)
консоль запросов позволяет получить исчерпывающие ответы.
...
вывод плана выполнения запроса, а также SQL-текст запроса, сформированного в СУБД. Для СУБД Microsoft SQL Server план выполнения выводится в виде дерева, а для остальных СУБД – в текстовом формате технологического журнала. Для упрощения анализа запросов также предусмотрено два режима отображения текстов запросов: с именами таблиц и колонок СУБД или с именами объектов метаданных и реквизитов конфигурации (только в обработке для "1С:Предприятие" версии 8.3).
44. Сан Саныч (herfis) 118 07.07.17 10:24 Сейчас в теме
Отличный слог, хорошие статьи. Пишите еще!
Ссылка на вашу "Под капотом управляемых форм" у меня вообще, можно сказать, в буфере обмена. Раздаю направо и налево.
Эта тоже хороша, жалко для меня пользы нет. Ждем продолжения :)
45. Сергей Беликов (HAMMER_59) 30 07.07.17 12:43 Сейчас в теме
(43)
Сами себе противоречите :(

В чём Вы видите противоречие?

Я утверждал, что узкое место можно обнаружить и не заглядывая в план запроса. Разве это не так?

Консоль позволяет вывести план запроса, и? С чего вы решили что раз позволяет, значит это крайне ценная информация?

Несерьезно какая-то у Вас тут атмосфера. Показал Вам инструмент, чтобы Вы колесо не изобретали. Сразу все на меня накинулись: "Да как я могу иметь другую точку зрения". У Вас тут что, секта?
46. Sergey Andreev (starik-2005) 1018 07.07.17 14:40 Сейчас в теме
(45)
Я утверждал, что узкое место можно обнаружить и не заглядывая в план запроса. Разве это не так?
Не совсем так. Например, в запросе, который вы крутите в консоли запросов, при отборе по высокоселективным данным у Вас может и не быть затыка, если же потом отбор измениться, то селективность другого значения может оказаться иной (низкой) и тот же самый запрос с тем же самым планом запроса, сохраненным в процедурном кеше, выполнится за весьма длительное время.

У меня на практике встречались случаи, когда реально скорость одного и того же запроса могла измениться со временем. Консоль, допустим, покажет, какой конкретно запрос (а подзапрос?) тормозит, но в реальной жизни может оказаться, что этот запрос тормозит только на определенных отборах, а на других работает хорошо. И механизм преодоления таких проблемных ситуаций лежит за пределами консоли запросов - он в профайлере, в планах запросов, в планах обслуживания, в создании нескольких запросов для разных отборов, чтобы планировщик для каждого из них подобрал оптимальный вариант, а не использовал сохраненный в процедурном кеше план.
47. Евгений Мартыненков (JohnyDeath) 290 07.07.17 23:14 Сейчас в теме
Неповторимый слог Андрея, повествующих о, казалось бы,замороченных вещах в очень доступной и простой форме.
Ты точно будешь удостоен вторым параллелепипедом!
48. Сергей Огородников (Serg O.) 133 08.07.17 00:12 Сейчас в теме
Отлично написано!
Объяснить сложные вещи простым языком - это дано не каждому.

На фразе "тут мы приближаемся к быстродействию тетеньки из регистратуры"
- ржал минут 10, это просто супер!!

Большой респект автору.
jif; JohnyDeath; +2 Ответить
Оставьте свое сообщение