Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Как мы автоматизировали анализ бенчмарков: опыт команды Postgres Professional

Дата публикации: 05-02-2026 18:02:47

Проводить нагрузочное тестирование PostgreSQL — полдела. Главная сложность начинается, когда нужно проанализировать сотни результатов и понять, где реальная просадка производительности, а где статистический шум. Младшие специалисты Postgres Professional Евгений Бузюркин, Дарья Барсукова и Рустам Хамидуллин разработали инструмент, который автоматически определяет тип распределения данных, детектирует мультимодальность и подбирает оптимальные параметры для каждого набора результатов бенчмарков. Читать далее

Основное содержимое страницы с новостью.

0949c3d2b27a1aad827ccdafe5462aaa.png

Производительность СУБД PostgreSQL — один из ключевых параметров, непосредственно влияющий на работу всей системы. Поэтому за производительностью тщательно следят, в том числе с помощью нагрузочного тестирования. Но провести тест — мало. Нужно ещё быть уверенным в достоверности полученного результата. И с этим нередко возникают проблемы.

Рассказываем, как команда Postgres Professional решила эту задачу и разработала инструмент, позволяющий автоматически проводить статистический анализ результата бенчмарка с учётом особенностей каждого набора данных.

Немного контекста

Производительность PostgreSQL напрямую влияет на всю систему. Медленные запросы и блокировки ухудшают пользовательский опыт, увеличивая задержки и вызывая ошибки. Неоптимальное использование CPU, памяти и дискового I/O перегружает сервер, замедляя другие процессы. При росте нагрузки недостаточная масштабируемость превращает СУБД в узкое место, требуя сложных решений вроде шардирования или репликации. Низкая производительность снижает надёжность системы в целом: возникают тайм-ауты, усложняется резервное копирование, нарушается работа микросервисов и очередей.

Любые просадки производительности PostgreSQL становятся поводом для вмешательства перформанс-инженера. Стандартный сценарий выглядит просто: сначала выполняется нагрузочное тестирование (бенчмарк), чтобы воспроизвести проблему, затем применяется профилирование и математическая статистика. Бенчмарк для PostgreSQL подразумевает нагрузку базы данных для измерения производительности и оценки количества транзакций в секунду (tps, transactions per second).

32aa1c7df6df201d4248b0a4b2df3f61.png

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

a5ec8be7a38a3813174f6962398c807c.pngОт контекста к проблематике

Проведение большого количества тестов порождает новую задачу: кто-то должен анализировать результаты целого массива данных.

0eb74a37e2ccb0089b79d56dcd8ae554.png

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

38dbdd20764e1a9ce7f1e264cd7df0e7.png

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

Поиск альтернативы

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

Например, мы можем взять выборку, отсортировать её на диапазоны (бины), а потом подсчитать количество значений в каждом диапазоне.

058f2e9971a19d88c5500a74f0726936.png

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

bd6aff687c8b73117f83e30eff80956a.png

Таким образом можно перевести изначальный график в формат гистограммы.

e438bdd84561fc3796d923f4905b47e7.png

Чтобы понять, что делать дальше, мы обратились к практикам статистики. Так, если взять бесконечную выборку с бесконечным количеством значений и на её основе нарисовать гистограмму с бесконечным количеством бинов, то получится функция. В статистике она называется функцией плотности распределения.

99f8de8ddc4ab5208c5525451c3882a8.png

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

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

  • Проверка теста на корректность. Например, мы прогоняем тест десять раз при идентичных сценариях и конфигурациях: девять раз получаем одно и то же распределение с погрешностью на параметры, а на десятый раз — отличное от них значение. Так мы можем обнаружить отклонение и исключить тест с ошибкой из выборки.

  • Сравнение. Допустим, мы запустили один и тот же сценарий сначала на PostgreSQL 16-й версии, потом — на 17-й, чтобы узнать, есть ли деградация в производительности. Определение типа распределения с параметрами способно в этом помочь.

Но как определить тип распределения? Теоретически это можно сделать вручную. 

Например, можно взять нашу гистограмму и попробовать переложить нормальное распределение на неё. При этом для функции нужны параметры. В качестве примера возьмём mean=9, std=4.

f0cc29b7b5faa7601d6e5dd692238349.png

Результат неплохой, но его потенциально можно улучшить. Для этого можно уменьшить стандартное отклонение (mean=9, std=3).

fc38e923ed530fd83641bdb9302b395d.png

Как видно, график существенно изменился. Но в этом главная проблема ручного перебора: сложно гарантировать точность построения графика. Нужна автоматизация. К ней мы обязательно вернёмся чуть позже.

Пока есть более значительная проблема — мультимодальность.

Немного о мультимодальности

В контексте обычных стандартных системных вызовов профилирование идёт гладко и прямолинейно, сложности исключены.

Но когда речь заходит о целых СУБД (например, таких как PostgreSQL), одним системным вызовом не обойдётся:  каждая функция и каждый системный вызов дают определённые нюансы, которые отражаются на гистограмме. В связи с этим график распределения зачастую имеет не один пик (мод), а сразу несколько. 

a7724e94235fb9421794922ed3111b27.png

Такое распределение с несколькими модами называется мультимодальным.

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

Наше решение для работы с мультимодальностью

Чтобы наглядно рассказать о работе с мультимодальностью, возможных «подводных камнях» и других издержках, рассмотрим реальный кейс из нашей практики.

К нам обратился клиент, который стал замечать просадку в tps во время поиска по таблице users. Сама таблица имела два формата:

  • в части строк имя и фамилия пользователей были разделены на две колонки (в одной — имя, в другой — фамилия);

  • в части строк имя и фамилия были записаны в одной колонке, а вторая оставалась пустой.

c7d6cac0a6a9bb5133762242292844f8.png

Первый формат можно считать хорошими данными, второй — плохими.

Чтобы разобраться с проблемой, мы, в первую очередь, постарались воспроизвести её у себя. Для этого провели нагрузочное тестирование: 30 секунд циклично обращались к хорошим данным, 30 секунд — к плохим. В итоге получили следующий результат работы бенчмарков.

dcdeaf70a57fd3c268d2e90852a0fe67.png

После этого на основе собранных данных мы построили гистограмму и получили мультимодальное распределение.

5ea2d1716f8c856dc2069d74ecf265b1.png

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

a3daf50514ebe1860186903d8ec4c6ba.png

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

Дискретизация

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

Если в таком случае отсортировать выборку и попытаться построить по ней график, то он будет содержать дыры и резкие пики.

c5aab5ee206224ddaeb53da75c7d1430.png

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

b50d902fc298a983d08605b1e05b781e.pngПредставление данных

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

Например, так выглядела наша исходная гистограмма.

eeb0758438b40e9e4861d721857ef3f9.png

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

bd8195bd5923696f86d51532b4e8b40b.png

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

Решение, которое позволяет обойти проблему с бинами в качестве параметров, исключить случайность и потерю информации придумал Андрей Акиншин. 

b855be0b91623f999ee0252cca95a8ce.png

Примечание. Подробнее с идеей и реализацией Андрея Акиншина можно ознакомиться здесь.

Его подход строится на основе методе квантильной оценки Харрелла—Дэвиса.

Чтобы понять логику этого метода, стоит вспомнить несколько базовых понятий. График функции плотности может учитывать бесконечное количество значений и иметь бесконечно узкие бины. В таком предельном случае гистограмма совпадёт с графиком функции плотности.

64faf5c781b17a845384b75ef93ee508.png

На этот график можно нанести квантиль — значение, которое делит исходное распределение на интервалы с одинаковыми вероятностями. Квантиль уровня 0,5, например, делит график пополам.

78abc4fa581931a3c0274c4f2fe64ba6.png

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

ab198e5d215b1babb46041160f9e7b19.png

В случае процентиля выборка делится уже на 100 частей.

7eb07b25128681855df8e9a3cfe018f9.png

Классический подход к поиску квантиля достаточно грубый. Если в выборке из десяти значений нужно найти 25-й процентиль — число, ниже которого лежит 25% данных, — берут второе или третье значение из отсортированной выборки. Этот метод сильно зависит от конкретных значений и даёт неустойчивый результат.

bcd1fbd1525e39cd4ebc2c75510af19c.png

Идея Харрелла—Дэвиса в том, что мы берём не одно конкретное значение, а всю выборку и даём всем значениям вес: чем ближе значение к искомому квантилю, тем больше его вес; чем значение дальше от него, тем меньше вес. В результате квантиль определяется как взвешенная сумма всех значений. 

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

В итоге, на метода Харрелла—Дэвиса мы смогли перейти к новому представлению данных.

Решение проблемы мультимодальности

После подготовки данных к дальнейшему анализу мы смогли приступить к решению главной проблемы на текущем этапе — разделению исходной выборки на моды.

ecd15e99d78c870c0ded5782953faf29.png

Изначально предстояло определить, что именно считать модами в контексте нашей задачи и нашего графа. Сложность в том, что строгого математического определения здесь нет и модой можно считать как глобальный максимум (он будет один), так и локальные максимумы (их будет несколько).

2dcd3bab877ea33cb15cb2444c77474d.png

Чтобы корректно определить все моды на нашем графике, мы воспользовались еще одной наработкой Андрея Акиншина.

Примечание. Подробнее с упомянутой наработкой Андрея Акиншина можно ознакомиться здесь.

В рамках метода мы представляем, что график функции плотности — горный пейзаж, который постепенно затапливается водой. Отсюда появляется несколько понятий:

  • моды — пики графиков;

  • горы — кривые графиков;

  • подземные воды — нижние части графиков; 

  • глубоководье — озёра, у которых площадь больше, чем площадь земли под ними. 

74a3f2d7a1b490291be705d110c0f402.png

Наш случай довольно простой:

  • построили квантильную оценку Харрелла—Дэвиса;

  • нашли все локальные максимумы;

  • разделили график по сегментам и заполнили их водой.

В результате можем быть точно уверены, что у нас всего две моды. Важно понимать, что этот алгоритм подразумевает учёт двух параметров:

  • чувствительность;

  • точность.

Сначала остановимся на первом.

Чувствительность позволяет понять, какое озеро считать глубоким. Например, в нашем случае при значении чувствительности 0,5 озеро считается глубоким, если его площадь больше всей площади водной поверхности.

S глубокого водоёма > 0,5 × (S глубокого водоёма + S грунтовых вод)

Вот здесь возникает проблема.

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

3c85ce7c2959ef543c07826dc8299203.png

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

ba47dcf36988c6aedeba6474e11e82ee.png

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

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

322c104ba579c7681efca58315bd5f3d.png

Для начала находим самые значимые моды (в нашем случае это вся зеленая зона) и исключаем их влияние.

7ed07ff2fca4553adbb868ee2c525d81.png

Далее работаем только с красной частью графика: запускаем алгоритм на ней и получаем результаты. 

196796268ba0f161a80317a003a1f2d9.png

Итоговым решением будет объединение этих итераций с двумя модами. При этом шумы не детектируются.

df3d3e30676c5fb516e95f73ae2b85e9.png

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

Таким образом, задачу разделения исходной выборки на отдельные моды, которые можно анализировать независимо, можно считать решённой. 

9dedeef7cd9558b22ab3b37fe2b2e285.png

После того как мы избавились от мультимодальности, можем работать только с унимодальными данными.

Теперь важно понять, какое распределение и с какими параметрами имеют наши унимодальные выборки.

52d591e1d98fcf65d4291368dfde5814.png

Начнём с анализа второй выборки.

Упрощение

Начнём решать задачу с упрощения. Оно нужно, поскольку существует большое количество разных распределений, и сразу сказать, к какому из них принадлежат наши данные, невозможно. 

Для удобства сократим количество рассматриваемых распределений до четырёх наиболее распространённых:

  • логнормальное;

  • Гумбеля;

  • Вейбулла;

  • Фреше.

04146d69f8cdb9111d7e046591ce5365.png

Но важно понимать, что форма каждого распределения зависит от определённого набора параметров. Для одного и того же распределения даже при незначительном отличии параметров форма может сильно меняться.

562eb2b7431b71037a134ee8374320ee.png

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

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

d901e0535e79e2a2167dc44fb8d7d2bf.png

Важно это сделать так, чтобы параметры не пришлось перебирать вручную.

c14962a483c68c5e1447bc025f411d5b.png

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

  • мы загружаем в функцию выборку и некоторое распределение;

  • функция возвращает число, которое указывает на то, насколько далеко заданное распределение от переданной выборки.

То есть если функция хорошо описывается на заданной плотности распределения, значение маленькое, если плохо — большое.

9fd2451f2d02ba903945186a82032c98.png

Подобную функцию можно получить из функции правдоподобия.

Примечание. Функция правдоподобия вычисляет плотности распределения для неких чисел и функций. То есть функция правдоподобия — просто значение функции плотности распределения в заданной точке. При этом функция характеризует только вероятность появления какой-то величины.

38fc420cfdae9ae4dc97437623fc13e2.png

Рассчитать подобную функцию довольно просто:

  • берём выборку, то есть результаты бенчмарков, которые формируют гистограмму, и принимаем их за Х;

  • считаем правдоподобие каждого элемента и просто перемножаем их.

В результате получаем функцию правдоподобия. 

x = {х1, х2, х3, …, хN}φ(Х) = f(x1)f(x2)*...*f(xN)

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

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

Теперь стоит задача получить функцию расстояния из функции правдоподобия. Тут всё просто, поскольку мы приняли два правила:

  • чем функция расстояния меньше, тем лучше;

  • чем функция правдоподобия больше, тем лучше.

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

Функция правдоподобия:

φ(Х) = f(x1)f(x2)*...*f(xN)ln(φ(Х)) = ln(f(x1) + ln(f(x2) + … + ln(f(xN) 

Результат минимизации функции расстояния:

-ln(φ(Х)) = -ln(f(x1) - ln(f(x2) - … - ln(f(xN) 

В математической статистике такие функции называют MD-оценками. Подобных оценок много, но мы реализуем несколько, чтобы понять, какая из них лучше работает в нашем случае. 

Дальше нам нужно найти параметры, наиболее оптимальные для нашего случая. Чтобы не делать это вручную, можно применить алгоритм минимизации, например, Covariance Matrix Adaptation Evolution Strategy (CMA-ES). 

6553adc543ed7d19685fd3087269499c.png

Выбор в пользу CMA-ES обусловлен преимуществами алгоритма, который:

  • не требует градиента (важно, поскольку не для всех MD-оценок мы можем рассчитать градиент);

  • может быть выбирана из локальных минимумов;

  • поддерживает оптимизацию по большому числу параметров;

  • позволяет устанавливать ограничения на область определения параметров.

У алгоритма есть и недостатки; 

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

  • Алгоритм работает долго, поэтому потребляет много вычислительных ресурсов.

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

967cb11361665f6b272aa28aabbb1113.png

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

В нашем случае лучшим было именно логнормальное распределение. 

6bbe9322bdc82bc5bf664ed620cd6e6f.png

Через аналогичный пайплайн мы прогнали и первое унимодальное распределение — в первом случае оказалось распределение Гумбеля.

c99e664d0bdbdcd0a27b843df887bc55.pngИтоговая гистограмма

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

ceaeb7851eb14e0f66455a532850ba1e.png

В итоге мы реализовали инструмент, который действует по уже отработанной и проверенной нами схеме:

  • собираются результаты бенчмарка;

  • результаты прогоняются через джиттеринг;

  • строится квантильная оценка Харрелла-Дэвиса для детектора мультимодальности;

  • детектируются моды;

  • каждая мода параллельно анализируется: для каждой подбираются параметры, каждая прогоняется через критерий согласия;

  • полученные данные объединяются в итоговое распределение. 

    6274b83cd1013d990e3bbc0b227d55ce.png
Краткое саммари

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

Поэтому мы решили идти другим путём. Мы разработали инструмент, в котором реализован целый набор алгоритмов: от джиттеринга и квантильной оценки плотности распределения Харрелла—Дэвиса до набора критериев оценки согласия. С его помощью мы можем автоматически проводить статистический анализ результата бенчмарка с учётом особенностей каждого набора данных, в том числе выявлять мультимодальность, количество и границы преобладания каждой моды, а также параметры распределений.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Промпты, RAG, LLM-тюнинг, Harness… Идём дальше?011.8711-06-2026
2Кэш результатов запросов в Postgres Pro: как ускорить часто выполняющиеся запросы и разгрузить базу07.1515-05-2026
3Кто выгрузил платежи, или Пример расследования инцидента на аудите в Postgres Pro Enterprise08.3710-07-2026
4Apache AGE под нагрузкой: что происходит, когда графы внутри PostgreSQL начинают по-настоящему тестировать011.6724-03-2026
5[Перевод] Нетипичные оптимизации в PostgreSQL, или Креативное ускорение запросов08.2102-03-2026
6Кто выгрузил платежи, или Пример расследования инцидента на аудите в Postgres Pro Enterprise0710-07-2026
7Сессионные вычислители — залог успеха аналитики будущего08.0527-02-2026
8В погоне за APDEX-ом, или как создать HighLoad на недорогом серверном железе116.3720-05-2026
9Полиморфные ссылки в PostgreSQL: помогаем СУБД избежать провалов производительности0730-06-2026

Классификация: Пресс-релизы. Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 7.49. Источник: habr.com.