Как стать автором
Обновить

Комментарии 22

интересно как выглядит план выполнения, если cделать только одно изменение — заменить OR на UNION + LIMIT 1 без остальных изменений?
Поскольку это в нашем примере самое больное место, то и эффект от него максимальный: 0.046ms/14 buffers

Вот она, главная боль sql — то, что задумывалось (и старается быть) декларативным описанием желаемого результата, хочешь, не хочешь, а выкидывает иногда такие коленца, что надо забыть об абстракциях, и вместо простого "чо хочу" приходится объяснять ему на пальцах, как не надо. И как хорошо, что подобные кудрявости далеко не на каждом запросе встают — читаемость после такой подкапотной оптимизации резко падает, и через полгода даже чтобы просто прочитать и понять приходится расчехлять бубен.
Что там Балу говорил Маугли про счастье? "Простые запросы", кажется? Даже жаль иногда, что этот принцип не совместим с реальностью...

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

Справедливости ради, хинты — признак беспомощности или недостаточного понимания что происходит в запросе.

Но такие танцы с бубном действительно что то очень странное)

Просто в вашей практике хинты не были нужны. Когда решение тиражируется на десятки и сотни инстансов, а на местах нет грамотных DBA, то хинты зачастую это единственный выход. Бывает что сбор статистики по какой-то причине вообще отключен.
Кроме того, бывает что система разворачивается в секретном контуре и доступ к ней имеют только федеральные агенты, которые однако ничего не смыслят в IT.
Ещё вариант — нет доступа к исходникам, а есть запрос, который нужно оптимизнуть. Тогда можно сохранить для него специальный план. Был такой случай, когда в топ AWR на огромном федеральном проекте постоянно вылезал запрос, выполняемый ядром Oracle Application Server, и даже у самих сотрудников Oracle не было возможности это починить иначе чем сохраненным планом.


Вообще, все дело в наличии актуальной статистики. Иногда, однако, в течение дня характер и распределение данных меняются кардиально. Например, система принимает и обрабатывает миллионы бизнес объектов, и в какой-то момент запускается операция обработки полученных данных (консолидация, аналитика, matching и т.п.), а статистика собиралась 8 часов назад, т.к. на больших БД это довольно длительная и затратная операция. Результат — оптимизатор выбирает неверный алгоритм соединения таблиц или неверный индекс и т.п.
Поэтому все мои знакомые DBA и senior developers, в том числе сотрудники Oracle, используют хинты не просто иногда, а как обыденный инструмент каждый день.


Ни один оптимизатор не может сравниться с человеком, имеющим десятки лет опыта, знающим всю подноготную этого алгоритма.

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

С другой стороны, я говорил в основном о хинтах в запросах, типа подсказок каким джоином джоинить, экспандить или нет и какие индексы брать. =)
Иногда, однако, в течение дня характер и распределение данных меняются кардиально.
вот с этим можно и нужно бороться, очевидно.
Результат — оптимизатор выбирает неверный алгоритм соединения таблиц или неверный индекс и т.п.
Для особо запущенных случаев, как было замечено, можно и план сохранить. Но всё же лучше решить проблему системно. Хотя бы потому что потом придёт новый разработчик, прогонит хинты на текущем состоянии базы и выбросить нафиг… )

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

ЗЫ:
сотрудники Oracle
В оракле, возможно, всё иначе. Хотя, вроде, у него планировщик получше MSного.

Первый антипаттерн — называть поля русским текстом. Читаемость нулевая.

А хинты не используете потому что прокладка не поддерживает хинты?

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


Допускаю, что большая часть документов создана этим сотрудником и поэтому индексы по «Сотрудник» и «Лицо3» не используются или их просто нет. Но в этом случае, искать «первый попавшийся» совсем странно.


Если же упомянутые индексы достаточно селективны, то PG будет использовать их. Буквально — пройтись по ветке индекса для заданного сотрудника и найти первый попавшийся документ подпадающий под условия, затем проверить его тип в другой таблице и завершить запрос если тип подошёл.


Типов документов может быть много, тогда индекс по (Сотрудник, Тип Документа) решит все проблемы.


В сухом остатке — я не вижу причин переписывать исходный запрос.

Почему первый попавшийся?
Потому что хотим проверить просто наличие такой записи. Например, если сотрудник еще ни разу не оформлял себе или кому-то отпуска — давайте покажем ему в интерфейсе подсказку, как сделать это правильно.
Если же упомянутые индексы достаточно селективны, то PG будет использовать их.… В сухом остатке — я не вижу причин переписывать исходный запрос.
Все планы сняты в одном и том же состоянии БД, то есть со всеми необходимыми индексами. И тем не менее — вот такое отличие в производительности.
Долгий Bitmap heap scan говорит о том, что документов у этого сотрудника много, причем большая часть из них удалена или черновик или состояние у документа не то. Можно предположить, что если включить в индекс одно из этих полей, то запрос начнет выполняться намного быстрее.

Изначальный план запроса, отрабатывает 144 ms, хотя нужна всего одна запись. Если мои предположения о характере данных верны, то возможно во всей базе и есть только одна подходящая запись и поэтому запрос такой долгий. В вашем финальном варианте довольно тяжело разобраться. Нет гарантий, что он быстрее только потому, что запросу везет и он встречает эту запись, начиная сканировать таблицу «где нужно». Во всяком случае я не вижу, почему финальный вариант быстрее.

Если пристально посмотреть на исходный план, то видно, что по всем дополнительным условиям в Bitmap Heap Scan отфильтровалось всего 1.5K записей из 53K. А до проверки состояния по связанной таблице дело вовсе не дошло (never executed), потому что не нашлось ни одной записи после соединения с ТипДокумента.
То есть всех документов по сотруднику — много, а нужного типа — нет.
Но планировщик хоть и понимает, что идентификатор типа у нас уникален (rows=1), пробросить в индекс его через InitPlan не пытается. Вот и стоит ему помочь.

Похоже у сотрудника очень много документов, но лишь малая часть из них это нужные нам планы. Почему бы не сделать частичный индекс по (сотрудник/лицо, тип документа) где тип документа == «план»?
Потому что список типов заранее не определен и расширяется самим пользователем. Так что под каждый отдельный индекс не придумаешь заранее.

LIMIT без ORDER BY? Выглядит не очень, чтобы очень.

А зачем нужен ORDER, если нам все равно какая запись?

А вы посмотрите план с ORDER BY и без. Мне, например, очень интересно, что будет.

Какой из вариантов запроса и по какому полю посортировать?

Вариант — любой, поле — индексированное.

Посортировал исходный по ТипДокумента — то же самое. Хотя есть индекс Документ(ТипДокумента, Сотрудник).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий