Tantor Postgres 18 - масштабный релиз СУБД, за которым стоят месяцы тестирования, сотни часов нагрузочных прогонов и десятки исправлений, о которых пользователь никогда не узнает просто потому, что они были найдены и устранены до выхода версии. Александр Симонов, руководитель направления развития 1С в "Тантор Лабс", рассказывает, как устроен процесс тестирования изнутри, почему одного "эталонного" прогона недостаточно, что делать, когда ванильный PostgreSQL 18 ломает собственные оптимизации, и как Tantor Postgres приближается к той планке, которую MS SQL Server держал годами.
Процесс тестирования релизов
Расскажите в целом, как выглядит процесс тестирования нового релиза Tantor Postgres перед выпуском?
А.С. - Все начинается задолго до выпуска - мы тестируем фичи, которые войдут в 18 версию, на версии предыдущей. Разработчик берет последний релиз 17 версии, накатывает на него тестируемую фичу, и отдает нам в центр экспертизы 1С на тест. Например, управление моделью оценки кардинальности мы начали тестировать еще прошлым летом. Изначально у нас было целых четыре модели для сравнения. Ранний старт тестов позволил нам провести тщательный анализ того, где хорошо, а где плохо показывает себя каждая модель оценки кардинальности, и в итоге до финального релиза дошло только две (а две другие модели показали на реальных базах 1С нестабильные результаты и были исключены). Таким образом, мы на 17 версии проверили примерно половину фич, который вошли в версию 18. Это значительно упрощает тестирование 18 версии, ведь часть работы уже проделана.
Следующий этап - тестирование alpha-версии 18й версии. Это самый ответственный этап, ведь на поведение планировщика могут влиять изменения в ванильной версии, и нередко это происходит не в лучшую сторону. Здесь мы новую версию стабилизируем: находим ошибки и проблемы планировщика, исправляем их, чтобы убедиться что все корректно работает. Также тестируем новые фичи, которые не протестировали на 17 версии, и тестируем всё вместе, чтобы исключить конфликты и обеспечить оптимальную работу.
Затем выходит beta-версия 18, по сути релиз-кандидат. Как правило, alpha- и beta-версии выходят как разные минорные релизы. Мы делаем финальные прогоны нагрузочных тестов. Цель - проверить на качество и производительность, исключить ошибки при включении всех фич и при различных вариантах настроек, убедиться, что не осталось ключевых операций, которые бы не ускорились, а замедлились. Качество и производительность мы проверяем на всех этапах.
Какие именно нагрузочные тесты вы запускаете? Что за конфигурации 1С, какое железо, сколько пользователей?
Наш нагрузочный стенд и некоторые тесты уже мелькали в наших статьях по 1С. Если говорить более подробно, то тестовый стенд специально выделен только под тестирование Tantor Postgres под 1С:
Роль сервера |
Количество |
OC |
Версия ПО |
Кол-во CPU |
Кол-во RAM (Гб) |
Сервер лицензирования 1С |
1 |
Astra Linux 1.7.5 |
1С Предприятие 8.3.27.1719 |
4 |
16 |
Сервер приложений 1С |
3 |
Astra Linux 1.7.5 |
1С Предприятие 8.3.27.1719 |
32 |
126 |
Терминальный сервер |
10 |
Astra Linux 1.7.5 |
1С Предприятие 8.3.27.1719 |
16 |
64 |
Сервер СУБД |
1 |
Astra Linux 1.8.1 |
Tantor SE 1C 18.x |
48 |
512 |
Мониторинг |
1 |
Astra Linux 1.7.5 |
Платформа Tantor 6.x |
4 |
16 |
Кластер серверов 1С состоит из четырех серверов: сервер лицензирования, один центральный и два рабочих. Клиенты нагрузочного теста запускаются на десяти терминальных серверах. Эти ресурсы, само собой, целиком задействованы не всегда, на некоторых тестах достаточно одного сервера приложений и двух терминальных. А если запускаем тест на 2-3 тысячи пользователей, то здесь уже используем все ресурсы. Со стороны СУБД у нас один сервер, реплика подключается в том случае, когда в ней есть необходимость, допустим, хотим проверить что-то связанное с репликацией или работой механизма копий баз данных 1С.
Нагрузочных тестов у нас несколько:
Конфигурация |
Размер базы (Гб) |
Количество пользователей |
Продолжительность теста (ч) |
ERP 2.5 |
646 |
349 |
1 час |
ERP 2.5 |
646 |
1 (Закрытие месяца/расчет себестоимости) |
2 часа |
ERP 2.5 |
1273 |
3200 |
10 часов |
Документооборот 2.1 |
210 |
362 |
3 часа |
ЗУП 3.1 |
50 |
276 |
2 часа |
Почему так много тестов? Разве недостаточно одного эталонного прогона?
Нет, каждый тест имеет свой профиль нагрузки и характерные для него запросы, которые могут не встречаться в других тестах. Начинаем тестирование с теста на ERP длительностью 1 час. Это самый быстрый тест, и он хорош тем, что почти всегда показывает, что изменения планировщика или ядра повлияли на производительность ключевых операций. Это позволяет быстро выявить потенциальные проблемы. Воспроизводим их, передаем в разработку, и после исправления запускаем тест еще раз и убеждаемся, что все наладилось. Также на этом тесте мы пишем сценарии под оптимизации планировщика, чтобы видеть от них эффект, и заодно проверяем, что новые изменения в планировщике и ядре не ломают наши собственные оптимизации. После этого можем переходить к следующему тесту.
Тест "Закрытие месяца" отличается тем, что в нем в рамках одного соединения с СУБД постоянно создается огромное количество временных таблиц и большое разнообразие запросов к ним. На этом тесте очень показательно тестировать оптимизации, связанные с временными таблицами, например, их параллелизм.
Первые два теста проходят на одной и той же базе, затем переходим к более длительным тестам - ЗУП и Документооборот (ДО). В ЗУП нас встречают регистры расчета, которых нет в других тестируемых конфигурациях, а в ДО активно используются бизнес-процессы.
Последний нагрузочный тест - опять на ERP, но уже на другой базе. Здесь мы задействуем все серверы тестового стенда, чтобы проверить работу нескольких тысяч пользователей. Его, как правило, запускаем на ночь или в выходные, а с утра анализируем результаты.
Полный цикл тестирования мажорного релиза занимает у нас 2-3 месяца, а минорного - 2-4 недели. Таким образом, ситуация, когда клиент обновился на новый релиз Tantor Postgres и 1С-система у него начала деградировать, для нас априори исключена.
Как мы разбираем, что изменилось
Как вы обнаруживаете, что какая-то операция стала работать медленнее? Что является "сигналом тревоги"?
Все просто: сравниваем 2 результата нагрузочного теста, так сказать, "до" и "после", и находим в этом сравнении операции, которые деградировали. Например:

Как видите, среднее время всех ключевых операций стало лучше на 3,1%, но это не значит, что все хорошо. Красным выделены ключевые операции, время выполнения которых замедлилось, а зеленым - которых ускорилось.
На корректность сравнения также влияют еще несколько факторов:
Не было ли в ходе нагрузочного теста узким местом оборудование. Например, если CPU на сервере приложений или СУБД был утилизирован на 80–90% или наблюдались кратковременные пики до 100%, результаты такого теста сравнивать некорректно: среднее время многих операций окажется хуже эталонного не по вине СУБД, а из-за банальной нехватки ресурсов - смежной нагрузки на хосте виртуализации или других внешних факторов. Такие результаты мы не засчитываем, разбираемся в их причинах и повторяем тест.
Ключевая операция должна выполниться в ходе теста не менее ста раз. Если операция выполнялась редко, её среднее время сильно подвержено случайным выбросам: одно-два аномально долгих выполнения могут существенно исказить итоговую цифру. При 100+ выполнениях такие выбросы сглаживаются, и среднее становится статистически значимым.
Как выглядит процесс разбора конкретной деградации от момента обнаружения до решения?
Здесь процесс одинаков независимо от того, ищем ли мы деградацию или ускорение.
На входе у нас - исследуемая ключевая операция.
Включаем логирование: сбор планов запросов более 100 мс, и сбор запросов в технологическом журнале с такой же длительностью.
Выполняем ключевую операцию в однопользовательском режиме в окружениях "до" и "после". В примере выше это будет на SE 1C 17.7 и SE 1C 18.1.
Анализируем лог, начинаем смотреть тот, где ключевая операция выполнялась дольше, ищем самые длительные по времени запросы.
Сравниваем их с логом, где ключевая операция выполнялась быстрее - таким образом находим запросы, скорость выполнения которых изменилась.
Сравниваем планы этого запроса при разном окружении - здесь уже понимаем причины, почему запрос стал выполняться дольше/быстрее. В большинстве случаев причины видны уже на этом шаге, но иногда планы идентичны. Тогда анализируем события ожидания и профиль выполнения глубже.
Если поведение ожидаемое, т.е. мы ожидали ускорение, то на этом расследование заканчивается - мы понимаем, что запрос ускорился благодаря конкретной фиче.
Если же запрос деградировал, то ставим задачу на разработку СУБД разобраться, какие изменения в исходном коде Tantor Postgres повлияли. Готовим разработчику мини-дамп базы, чтобы он мог воспроизвести данный запрос в своей среде разработки, а не на тестовом сервере.
После выполнения задачи на разработку сначала прогоняем локально ключевую операцию, чтобы убедиться, что она ускорена, а потом уже запускаем целиком нагрузочный тест, чтобы убедиться, что доработка на другие ключевые операции не повлияла.
Выше вы говорили, что изменения из ванильного PostgreSQL 18 сделали что-то хуже в контексте 1С. Можете привести конкретные примеры?
Да, здесь было несколько примеров. При первом тесте на базе ERP длительностью 1 час мы увидели несколько ключевых операций, которые стали выполняться дольше, при этом, на общий APDEX это почти не повлияло. А вот на другом нагрузочном тесте на базе ERP длительностью 10 часов мы уже получили APDEX на 15% хуже, чем на 17 версии Tantor Postgres. Повлияло в ванили вот что. У планировщика запросов есть такое понятие как RTEKind - это внутренняя "метка" в PostgreSQL, которая говорит планировщику, с чем именно он сейчас работает в запросе. Разбирая SQL-запрос, он встречает разные "источники данных" - это может быть обычная таблица, подзапрос, CTE (WITH-выражение), результат JOIN-а и так далее. Каждому такому источнику присваивается тип - это и есть RTEKind (Range Table Entry Kind, то есть "тип записи в таблице диапазонов"). В 18 версии появился новый RTEKind - RTE_GROUP. Наши оптимизации об этом ничего не знали, в результате планировщик воспринимал RTE_GROUP как независимое отношение и считал предикаты некоррелированными, стал сильно ошибаться с оценкой количества строк и выбирать неоптимальные планы.
Исправление на первый взгляд оказалось простым, но найти корень проблемы потребовало времени: добавить RTE_GROUP в ту же проверку, где обрабатываются подзапросы и прочие «нетабличные» отношения. После этого планировщик снова стал корректно учитывать корреляцию и выбирать оптимальный план.
Следующий случай был связан с io_method, который появился в 18 версии. Его можно установить в значение sync, т.е. также как было в версии 17, но запрос последовательного сканирования большой таблицы (20+ млн записей) стал выполняться на 10% медленнее. Анализ собранных утилитой perf данных показал, что в 18 версии синхронное чтение работает медленнее из-за того, что появился дополнительный оверхед нового AIO-слоя (staging, управление структурами io_uring/AIO). При этом если включить асинхронное чтение (io_method = worker), то последовательное чтение такой таблицы выполняется быстрее, чем на 17 версии.
Также мы портировали из 19 версии оптимизацию eager aggregate. Мы постоянно смотрим "по сторонам", чтобы найти какие-то интересные оптимизации планировщика, которые позволят сделать нашу СУБД лучше. В ходе тестирования выяснили, что модель оценки стоимости этой оптимизации требует доработки под 1С, т.к. она использовалась в том числе там, где ее использование было неоправданным. Проблема проявлялась в запросах, где планировщик до этого выбирал оптимальный индекс и читал минимальное количество записей. После включения оптимизации он начинал использовать менее подходящий индекс и вычитывать значительно больше записей, рассчитывая сократить итоговую выборку за счёт eager aggregate, но в итоге получая обратный эффект.
В итоге после доработок APDEX вернулся на уровень 17 версии. Важно понимать, что это лишь один из этапов тестирования, на котором мы оцениваем влияние изменений ванильного PostgreSQL на наши оптимизации под 1С и устраняем возникшие проблемы.
А есть изменения из ванильного PostgreSQL 18 которые сделали что-то лучше?
Да, такие примеры тоже есть. Index Skip Scan ускоряет запросы, в которых пропущен предикат по одному из первых полей индекса. Вместо полного сканирования индекса планировщик итеративно находит возможные значения пропущенного поля и использует их как дополнительное условие, фактически перепрыгивая через незаданный предикат. В 1С такие запросы встречаются часто, и эффект может быть очень заметным: на наших тестах один из запросов ускорился в 250 раз - с 3 секунд до 13 мс - на том же индексе и без каких-либо изменений в самом запросе. Однако у этой оптимизации есть указанный в исходном коде недостаток: она исходит из пессимистического сценария и предполагает, что естественной корреляции между столбцами не существует, что для 1С не всегда хорошо. На нагрузочных тестах мы нашли один запрос, который из-за этого стал выполняться дольше. Пока оставили как есть, поставив задачу на анализ и доработку.
Также изменилась логика использования узла Memoize - нагрузочные тесты показали несколько случаев, где он перестал использоваться, потому что его применение ранее было ошибочным (Hits: 0, то есть кеш не давал никакого эффекта). Отказ от такого узла ускорил часть запросов, но, что удивительно, часть замедлил. Объясняется это просто: раньше планировщик случайно выбирал более быстрый план. Теперь план стал правильным, но чуть медленнее. Мы расцениваем это как приемлемый компромисс и оставили поведение как есть - правильнее здесь работать над качеством статистики, чтобы планировщик изначально имел меньше шансов ошибиться.
Как вы разграничиваете: это баг ванильного PostgreSQL, ваша собственная проблема или баг платформы 1С?
Ответить на первую часть вопроса может только разработчик СУБД, анализируя найденную проблему. В ходе анализа он находит проблемный участок кода, из-за которого проявляется проблема, а определить далее это наш код или изменения из ванилы как правило не составляет труда. Что касается второй части вопроса, то если мы находим кейсы, где проблема на стороне платформы 1С, то также готовим тестовый пример и отправляем его в фирму 1С. Из последних кейсов, которые отправили, могу отметить два: повышенное потребление памяти на сервере приложений при использовании механизма копий БД, а также предложение по ускорению записи в таблицы итогов регистра бухгалтерии. Что интересно, ускорить запись в таблицы итогов можно как доработав платформу 1С, так и СУБД. Мы со своей стороны также планируем доработать СУБД, и планируем рассказать об этом в отдельной статье.
От проблемы - к фиче
Как вообще рождается задача на оптимизацию? Это жалоба клиента, результат теста или что-то ещё?
Чаще всего задача исходит из разбора кейса клиента, но есть и другие инициаторы задач на оптимизацию. Я бы описал процесс такой блок-схемой:
Пару месяцев назад у нас было сразу два кейса у одного клиента, где после миграции с MS SQL на Tantor Postgres действия пользователей стали выполняться дольше. Мы получили от них планы запроса, разобрали их, воспроизвели аналогичные запросы на MS SQL, поняли, почему там запросы выполняются быстрее, и поставили задачи на реализацию этих же оптимизаций в планировщике нашей СУБД.
Разбор результатов нагрузочных тестов - как внутренних, так и внешних с подрядчиками, - тоже помогает находить идеи, где и что можно улучшить. Например, разбирая длительные запросы нагрузочного теста на 30 тыс. пользователей, мы наткнулись на шаблон запроса, который был топ-1 по общей длительности. Оптимизировать его было довольно просто, если бы была возможность переписать текст запроса. Мы научили наш планировщик делать это, и правильность выбранного пути подтвердилась тем, что такая же техника была реализована в MS SQL и Oracle - см. материал "Оптимизация дизъюнктивных подзапросов".
Что касается идей разработчиков и R&D, то на мой взгляд, работа разработчика СУБД - это в каком-то отношении постоянное R&D, потому что PostgreSQL - сложная инженерная система, где каждое изменение должно соответствовать высоким стандартам сообщества. Чтобы добавить одну оптимизацию планировщика, нужно глубоко понимать, как взаимодействуют десятки подсистем: как строится дерево запроса, как работает оценка стоимости, как принимаются решения об использовании индексов, параллелизма, материализации. Поэтому прежде чем написать первую строчку кода по задаче, разработчик тратит время на изучение того, как устроена та или иная часть ядра, какие у неё ограничения и почему она работает именно так, изучает треды на PostgreSQL Hackers (pgsql-hackers), смотрит, не предпринималось ли попыток решить подобную задачу. В случае задач 1С - это еще более обширное R&D потому, что некоторые задачи решены в MS SQL или Oracle, и многие их решения описаны в патентах или научных статьях, которые требуется изучить. Например, идея автоматического создания статистики как раз и была получена в результате такого исследования.
Также у нас есть выделенное R&D-подразделение, и в части 1С мы работаем с ними над рядом фундаментальных задач, но об этом отдельный разговор.
Можете привести свежий пример — от момента «что-то пошло не так» до готовой фичи в релизе?
Да, приведу пример как раз из 18 релиза. На одной из наших тестовых баз ERP при выполнении закрытия месяца воспроизводился запрос длительностью 10 минут. По плану запроса было видно, что планировщик при выборе плана не учитывает то, сколько строк ему придется отфильтровать при поиске в индексе (большое значение Rows Removed by Filter). Здесь возникает задача в Jira с примером неоптимального запроса, но в разработку ее пока не отдаем.
В ходе различных нагрузочных тестов и обращений клиентов возникают еще подобные планы запроса с такой же характерной проблемой. Мы обогащаем нашу задачу новыми примерами, чтобы подтвердить, что решается не какой-то один частный кейс, а действительно часто встречающаяся проблема. А когда у одного из клиентов обнаружился аналогичный запрос со временем выполнения 3 часа, а на MS SQL он выполнялся менее минуты - приоритет задачи вырос сам собой.
Следующий этап - это анализ задачи разработчиком, поиск решения. по сути R&D. Мы нашли решение в pgsql-hackers, опробовали его, и оно действительно решило проблему исходного запроса, но не все кейсы. К тому же, на нагрузочных тестах оно показало плохие результаты, поскольку повлекло деградацию выполнения части запросов. Во время проведения нагрузочных тестов появилась другая идея для решения этой проблемы. Мы обсудили ее с разработкой, реализовали, убедились в том, что она решает все кейсы, описанные в исходной задаче, и не приводит к деградации других запросов. Так появилась оптимизация "Изменение логики расчета стоимости операций Index/Bitmap index scan". Стоит отметить, что нагрузочные тесты позволяют подбирать оптимальные значения новых параметров планировщика - в данном случае это был cpu_filter_cost, влияющий на расчёт стоимости плана запроса.
Как вы решаете, что оптимизацию нужно реализовывать в СУБД, а не советовать клиенту переписать запрос или перенастроить 1С?
Базовый принцип - клиент не должен подстраиваться под СУБД. Если запрос корректен с точки зрения логики 1С и на MS SQL работал быстро, то просить клиента переписывать его означает перекладывать на него нашу работу. Поэтому мы всегда сначала ищем решение на стороне СУБД, и только если запрос объективно написан плохо, даём рекомендации по его улучшению. Всё остальное зависит от конкретной ситуации: если видим, что SQL-запрос написан явно неоптимально, то запрашиваем у клиента текст запроса на языке 1С и даём рекомендации, как его переписать. Если же в запросе переписывать нечего, например, конфигурация типовая и находится на поддержке, или на MS SQL тот же запрос работал быстро - проводим анализ, как помочь клиенту без изменения запроса. Иногда достаточно скорректировать настройки СУБД, если они не были выставлены согласно нашим рекомендациям. Но оптимальный вариант в таких случаях - получить обезличенный дамп таблиц, участвующих в запросе, воспроизвести проблему на своём стенде и проработать варианты решения. Если получить дамп невозможно - воспроизводим запрос на своих тестовых базах согласно логике плана запроса, с опытом это не составляет труда. Лично мне, в этом хорошо помогает ИИ: по некоторым планам запроса, если правильно объяснить проблематику, он неплохо справляется с генерацией тестовых данных и подготовкой сценариев воспроизведения.
Сравнение с MS SQL Server
Вы упоминали случай, когда у одного клиента MS SQL работал быстрее сразу в двух сценариях. Как вы относитесь к такому?
Сам факт того, что MS SQL в каких-то сценариях работает быстрее - это нормально, мы не питаем иллюзий: MS SQL разрабатывается десятилетиями, и за это время его планировщик вырос до очень высокого уровня. За ним стоят годы научных исследований и сотни опубликованных работ. Однако было бы странно получить такой кейс от клиента и ничего с этим не сделать. Главная задача моего подразделения - из каждого кейса клиента генерировать вопрос "а что мы можем улучшить в нашей СУБД?" и конкретный практический ответ на него. На текущий момент по этим двум кейсам у нас в тестировании находятся оптимизации, и они попадут в следующий минорный релиз Tantor Postgres 18.x.
В чём главный принцип, к которому вы стремитесь при работе под 1С? Как он соотносится с подходом MS SQL?
Главный принцип - "настроил и забыл", то есть также как было в MS SQL. При работе с MS SQL не было такого паттерна поведения, что планировщик может быть в чем-то не прав, и требуется его доработка или изменение настроек. Конечно, если миграцию с MS SQL сопровождает эксперт 1С - проводится нагрузочное тестирование, анализируются и оптимизируются долгие запросы - то риски того, что после миграции потребуется что-то донастраивать или переписывать, значительно снижаются. Но такие миграции скорее исключение, чем правило. Поэтому мы активно дорабатываем планировщик и ядро Tantor Postgres под специфику 1С. У нас есть план задач и мы планомерно движемся к цели. Важно, что это не только наша инициатива - это стратегический приоритет компании.
Почему привязка планов запросов, использование хинтов и аналогичные инструменты - это плохой путь для 1С?
Все эти инструменты объединяет одно: они не решают проблему, а маскируют её. Привязка плана запроса, использование хинтов, создание индексов в обход платформы 1С - все это костыли, которые работают ровно до тех пор, пока не изменится что-то вокруг них. А измениться может многое: если меняется текст запроса - меняется QueryID, и привязанный план перестаёт применяться. Добавляется новый индекс - планировщик начинает рассматривать новые варианты плана, и "зафиксированный" план или индекс может оказаться уже не оптимальным. Меняется структура таблицы - и индекс, созданный в обход платформы, удаляется. В лучшем случае запрос просто вернётся к прежней плохой производительности, в худшем - станет ещё хуже.
Отдельная проблема - организационная. Очень часто за применение подобных инструментов и за доработку конфигурации 1С отвечают разные люди. И когда после очередного обновления 1С запрос деградирует, то бывает сложно быстро определить, что именно изменилось: конфигурация, платформа 1С, структура данных или тот самый костыль, о котором уже все забыли. Поэтому наш подход - решать проблему в планировщике раз и навсегда, чтобы она не зависела от человеческого или организационного фактора.
Есть ли сценарии, где MS SQL Server фундаментально лучше, и это не вопрос одного патча?
Да, такие моменты еще есть, и именно они и составляют ядро нашей долгосрочной работы. Например, задачи, связанные с планированием запросов и статистикой, за один спринт не решаются. Подробнее расскажем об этом на Tantor JAM в начале осени.