🐘 Подготовленные планы в PostgreSQL

Кто-то меня спрашивал о выгоде преподготовленных планов в «Постгресе». Наши испытания показывают, что выигрыш порядка 10—15%, что очень немало. И я уже рассказывал, что придумал рецепт как делить их в условиях «умирающего» ПХП.

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

Какой выход? Краткий ответ: пока никакой.

Самый очевидный — игнорировать такие ошибки. К сожалению, он не подойдёт — если ошибка произойдёт в транзакции, «Постгрес» её просто откатит. Второй выход — не использовать подготовленные планы. Я пока нашёл третий — мы используем планы только если уверены, что они пойдут на мастер — это запросы на изменение чего-либо.

С другой стороны у меня появилась идея — добавить в эскуэль конструкцию PREPARE IF NOT EXISTS, ребята из, с которыми я обсуждал эту идею, PostgresPro уже сделали несколько версий патча.

Основа идеи — всегда слать запрос на компиляцию плана, но если он уже был скомпилирован, ничего не делать. Мы его уже погоняли (оттуда и цифры первого абзаца), но на этом пути больше трудностей, чем себе можно представить — в частности, надо патчить ещё и продукты для балансировки и расширять его клиентский протокол.

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

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

pavelpat № 1
А делать savepoint перед такими запросами на поготовку запросов и делать rollback to savepoint если произошла ошибка сильно дольше, чем выигрыш от подготовки?
Евгений Степанищев (bolknote.ru) автор № 2
Во-первых, правильное решение, всё же — предоставить возможность не подготавливать планы, если они уже готовы. Тем более, это в традициях Постгреса — что-то похожее есть почти во всех командах создания чего-либо.

Во-вторых, как ты думаешь, какова сложность операции отката/накатки транзакции и репликации всего этого? Не думаю, что стоит даже начинать копать в этом направлении.
hshhhhh.name № 3
Я не знаю что такое планы в постгресе, но ведь можно делать вместо 'plan_name' костыль 'server_0_plan_name', на каждый сервер будет хранится своя копия плана
Евгений Степанищев (bolknote.ru) автор № 5
Я не знаю что такое планы в постгресе

Планы в «Постгресе» то же, что и в любой другой базе.

pavelpat № 6
Во-вторых, как ты думаешь, какова сложность операции отката/накатки транзакции и репликации всего этого? Не думаю, что стоит даже начинать копать в этом направлении.

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

Олег Горбунов № 7
Комментарий для Евгения Степанищева:

Обидно еще то, как я понимаю, подготовленный план гораздо выгоднее в сложных селектах?
Евгений Степанищев (bolknote.ru) автор № 8
Обидно еще то, как я понимаю, подготовленный план гораздо выгоднее в сложных селектах?

Да, конечно!

Евгений Степанищев (bolknote.ru) автор № 9

Комментарий для pavelpat:

Насчёт стоимости savepoint'а, ни разу не слышал, что этот механизм не стоит использовать из-за каких-то проблем с производительностью.

В тех местах, где его стоит применять, никакого выбора нет. Там так не стоит вопрос.

Считаю, что этот вопрос нужно изучить, прежде чем отвечать.

Ну что ж, изучай.

alxt № 10
Надо будет сохранить этот пост и давать на него ссылку при вопросе «почему PHP не подходит для production».
Простейшие для нормальных языков вещи- и делаются через ж..у.

PS: поэтому и ctrl+enter не отправяет комментарий? :D
Евгений Степанищев (bolknote.ru) автор № 13
Надо будет сохранить этот пост и давать на него ссылку при вопросе «почему PHP не подходит для production».
Простейшие для нормальных языков вещи, и делаются через ж…у.

А как в других языках это сделать? Как какая-то нибудь Java узнает куда pgpool кинул запрос?

PS: поэтому и ctrl+enter не отправяет комментарий? :D

Да ленюсь всё починить :)

pavelpat № 11
«почему PHP не подходит для production».

А причём тут именно php? То, что пулер коннекшнов может кинуть вас на другой сервер, где prepared statement-а ещё нет, как-то относится к пхп?

Евгений, а почему нельзя просто использовать PgBouncer в режиме «Session pooling» для пула, а в конце каждой сессии делать DEALLOCATE подготовленного запроса? Разве это не решит проблему?

silent № 12
Комментарий для Евгения Степанищева:

pgbouncer позволяет при коннекте выполнять определенный запрос, можно запихнуть туда вызов функции, которая создаст все нужные планы
Евгений Степанищев (bolknote.ru) автор № 15
pgbouncer позволяет при коннекте выполнять определенный запрос, можно запихнуть туда вызов функции, которая создаст все нужные планы

Как узнать какие планы нужные?

Евгений Степанищев (bolknote.ru) автор № 14

Комментарий для pavelpat:

Евгений, а почему нельзя просто использовать PgBouncer в режиме «Session pooling» для пула, а в конце каждой сессии делать DEALLOCATE подготовленного запроса? Разве это не решит проблему?

Это убъёт всю идею кеширования планов — всё будет парситься заново.

silent № 16
Комментарий для Евгения Степанищева:

всегда есть какие-то наиболее частые и сложные запросы в системе, обычно для них планы и строят
Евгений Степанищев (bolknote.ru) автор № 17
Комментарий для silent:

Да их сотни. И планы надо на всё строить, что с биндами.
alxt № 18
> А как в других языках это сделать? Как какая-то нибудь Java узнает куда pgpool кинул запрос?

А зачем знать, если у меня пулл потоков, а не дохнущие одноразовые? Можно вообще не заморачиваться — будет прогрев нескоько дольше, и всё.

Евгений Степанищев (bolknote.ru) автор № 19
Комментарий для alxt:

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