Pull to refresh
183
0
Боровиков Кирилл @Kilor

Архитектура ИС: PostgreSQL, Node.js и highload

Send message

Способ любопытный. Правда, опирается на отсутствие \n в исходной строке и ; в заменах.

Объём информации, сохраняемой в pg_statistic командой ANALYZE, в частности максимальное число записей в массивах most_common_vals (самые популярные значения) и histogram_bounds (границы гистограмм) для каждого столбца, можно ограничить на уровне столбцов с помощью команды ALTER TABLE SET STATISTICS или глобально, установив параметр конфигурации default_statistics_target. В настоящее время ограничение по умолчанию равно 100 записям. Увеличивая этот предел, можно увеличить точность оценок планировщика, особенно для столбцов с нерегулярным распределением данных, ценой большего объёма pg_statistic и, возможно, увеличения времени расчёта этой статистики. И напротив, для столбцов с простым распределением данных может быть достаточно меньшего предела.

Понятно, что речь и в документации идет про 100 записей значений гистограммы, а не 100 исходных. Для начинающего разработчика, на мой взгляд, и про коэффициент 300, и про увеличение времени анализа - избыточно. На эти грабли он наступит еще ой как нескоро.

Там были еще курсы по разработке бизнес-логики на python, интерфейса на JS и управлению сервисами, но они слишком сильно "заточены" на нашу внутреннюю инфраструктуру, поэтому вряд ли будут публиковаться. Возможно, их следующие версии.

На схеме нарисовано, что ИНН - это PK в company. Где тут про ОГРН?

Ну, и в первом варианте jsonb-объект с единственным ключом inn ищется в phone/email в массиве... очевидно, состоящем из объектов такой же структуры?

Если в этой системе inn является уникальным ключом, то зачем в phone/email хранятся какие-то jsonb-объекты? Простого массива inn'ов разве недостаточно? Тогда они и искались бы эффективнее исходно.

При сравнении производительности запросов неплохо бы приводить планы, иначе может оказаться, что все "тормоза" первичного варианта вызваны исключительно стартовой незакэшированностью данных (shared read).

Какая уж тут магия? Все вполне объяснимо: gist достаточно быстро "схлапывается" до нужного прямоугольника, где находятся только искомые точки, а вот btree, фактически, для каждого подходящего значения dt делает вложенный поиск интервала по sum, что явно не быстро, поскольку линейно растет с количеством уникальных подходящих значений dt в интервале.

То есть нашли мы, например, значение dt = '2023-12-15', полезли искать внутри по sum - там ничего подходящего, а время уже потрачено.

Относительно составного по времени он тут выиграл 5.03 против 5.30, и 410 против 461 по страницам - то есть "копейку". Но выиграл ровно потому, что "двихдиапазонный" поиск по btree заведомо неэффективен.

А вот gist как раз выигрывает за счет более оптимизированного хранения самого дерева индекса - из heap страницы-то читались все разы одни и те же 338 за примерно 0.28-0.30мс.

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

https://explain.tensor.ru/archive/explain/f0d4564d9536a996688fe0b96a4a87b2:0:2024-03-15

Эти картинки в большом количестве присутствуют в упоминаемой статье.

Конечно, спасибо, поправил.

Железо надо покупать тогда, когда вы уперлись в какой-то ресурс (производительность CPU или диска, объем памяти) и не знаете способа, как уменьшить его потребление.

Например, 200 постоянно активных сессий будут хотеть много памяти (каждый коннект - процесс PG). Если у вас это количество выросло с 100 до 200, то и памяти понадобится вдвое.

С другой стороны, можно поставить pgbouncer в transaction mode, что сократит кол-во процессов до кол-ва активных транзакций.

А 1Ktps - не метрика производительности, поскольку они могут делать просто SELECT 1;, а это ниочем

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

  1. Если вас напрягает "сервер где-то в России", то можете настроить отправку из плагина на зеркало explain-postgresql.com "где-то в Amazon" или развернуть self-hosted версию где-то на собственных мощностях и отправлять туда.

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

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

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

В расширенной версии добавляется возможность массового сквозного анализа всего лога сервера PostgreSQL, в том числе в онлайн-режиме.

Результату деления, конечно. Спасибо, поправил.

если в запросе имеется несколько UNION, каждый очередной UNION будет объединять текущую выборку с выборкой, которую получил предыдущий UNION

Формально, X UNION Y UNION Z транслируется как (X UNION Y) UNION Z - так что не вижу смысла тут усложнять описание, про приоритет и так написано.

Это позволяет ускорить получение уникализованной сборки нескольких наборов записей, применив UNION DISTINCT только на самом последнем объединении

Хорошее замечание, но это уже к теме следующей лекции.

Как-то результат не совпадает с опубликованным в статье. Что я не так делаю?

Переписал абзац, поправил слайд. Спасибо!

просто UNION, но его внутри рекурсии стоит использовать с осторожностью ... Непонятно, из-за чего тут требует осторожничать.. или есть ещё какие-то резоны?

Приходилось встречать код примерно с таким смыслом:

WITH RECURSIVE T(i) AS (
  VALUES(1)
UNION
  SELECT
    j
  FROM
    T
  , generate_series(1, T.i + 1) j
  WHERE
    i < 10
)
TABLE T;

Он, конечно, корректно работает, но ни разу не эффективно. Но "простой" UNION маскирует эту проблему.

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

Есть и обратная сторона, тоже сталкиваюсь: "Фу, как это для меня сложно, даже читать дальше не буду!"

Раз уж пишете туториал, то стоило бы рассмотреть все возможности GROUPING SETS, включая и [DISTINCT] ROLLUP.

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

Причин три:

  • некоторые все еще живут на 9.4 или на форках от еще более ранних версий, где их нет

  • некоторые ORM "не разумеют" GROUPING SETS, а задача-то никуда не делась

  • показать разницу для тех, кто "я так привык, мне и так удобно", и, например, вместо HAVING при группировке используют подзапрос + WHERE

Достаточно просто идти вперед и счетчик всегда под рукой.

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

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

Гораздо проще сделать fetchOne в переменную как раз. То есть подобные "преимущества" никак не оправдывают ни лишней работы СУБД, ни раздувания объема resultset, ни избыточных затрат на его разбор на стороне БЛ.

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

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

1
23 ...

Information

Rating
Does not participate
Location
Ярославль, Ярославская обл., Россия
Works in
Date of birth
Registered
Activity