PostgreSQL для 1С-администратора: медленный запрос в pg_profile и правка конфига

30.09.26

База данных - HighLoad оптимизация

Разбор медленного запроса 1С на PostgreSQL 16: pg_stat_statements, отчет pg_profile за окно инцидента и postgresql.conf под OLTP. Установка, диагностика и отчеты из архива прогнаны на PostgreSQL 16.15 с pg_profile 4.16: сервер стартует с готовым конфигом, снимок создается, отчет сохраняется. Цифры кейса «Торговый контур» взяты с контура автора и на стенде не воспроизводились.

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
PostgreSQL для 1С-администратора: медленный запрос в pg_profile и правка конфига
.zip 13,78Kb
0 2 500 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Вы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».

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

Предельные режимы. Часть 2. Профилирование и настройка PostgreSQL: анализ ожиданий, pg_stat_statements, pg_profile

Цикл: Инженерный контур 1С - проектирование, эксплуатация и масштабирование enterprise-систем

Обзор цикла: Развитие высоконагруженной системы 1С:Предприятие 8.3 на PostgreSQL и ОС Linux. Часть 1: конвейер Vector, ClickHouse, Grafana.

В первой части мы развернули конвейер Vector → ClickHouse → Grafana и научились видеть на дашборде всплески TLOCKS, TTIMEOUT и медленные SDBL-события. Теперь у нас есть ответ на вопрос «что тормозит» - конкретный запрос к СУБД, конкретный пользователь, конкретная строка контекста 1С.

Следующий вопрос инженера: «почему этот запрос выполнялся 14 секунд?» Журнал регистрации и технологический журнал 1С здесь бессильны - они не знают ничего о внутреннем устройстве PostgreSQL: планах выполнения, статистике таблиц, буферном кэше и конкуренции за блокировки строк. Для ответа нужны инструменты самой СУБД.

В этой части мы пройдем полный путь: от включения расширений статистики до конкретного измененного параметра в postgresql.conf, который убрал Seq Scan с регистра накопления на 600 миллионов строк.

Целевая аудитория и контекст

  • Профиль читателя: DBA и ведущие 1С-разработчики, отвечающие за производительность баз данных; SRE-инженеры, эксплуатирующие высоконагруженные инсталляции на PostgreSQL.
  • Исходное состояние: Сквозной кейс «Торговый контур»: PostgreSQL 16, Linux, база 1.8 ТБ, 350 активных пользователей в пике. Конвейер телеметрии из Части 1 развернут и работает.
  • Ограничения: Никакие аналитические инструменты не должны выполнять тяжелые запросы на продуктивной СУБД. Все профилирование - пассивный сбор статистики, которую PostgreSQL накапливает сам в ходе штатной работы.

Постановка проблемы

После внедрения дашборда Grafana инженер видит на экране следующую картину в часы пиковых отгрузок:

  • SDBL-события с duration 8-14 секунд, связанные с проведением документов реализации.
  • Контекст указывает на общий модуль РегистрыНакопления.ТоварыНаСкладах - операция списания остатков.
  • В ClickHouse поле sql содержит текст запроса к временным таблицам и регистру накопления.

Текст запроса есть. Но без плана выполнения (EXPLAIN) и статистики СУБД невозможно ответить:

  • Использует ли планировщик индекс или идет Seq Scan?
  • Сколько страниц буфера было прочитано с диска (Disk Hits vs Buffer Hits)?
  • Нет ли конкуренции за RowExclusiveLock на строках таблицы регистра со стороны параллельных транзакций маркировки?

Инструмент для ответа на эти вопросы - расширения pg_stat_statements и pg_profile.

🔍 Архитектура наблюдаемости на уровне СУБД

Наблюдаемость PostgreSQL строится на трех независимых уровнях:

Рис. 1. Три уровня наблюдаемости PostgreSQL

Ключевой принцип: все три уровня - пассивные представления (views). PostgreSQL собирает их в разделяемой памяти в ходе обычной работы. Мы не добавляем нагрузки - мы читаем уже накопленные данные.

🔍 Включение pg_stat_statements без деградации продуктива

2.1. Оценка накладных расходов

pg_stat_statements добавляет к каждому запросу операцию нормализации текста и атомарное обновление счетчика в shared memory. На нагрузке «Торгового контура» (350 пользователей, смешанные OLTP-транзакции) измеренный оверхед составил менее 0.8% по CPU - значительно ниже порога чувствительности бизнес-процессов.

Расширение безопасно включать на продуктиве. Единственное ограничение: параметр pg_stat_statements.max должен быть достаточно большим, чтобы не вытеснять важные запросы из кэша статистики.

2.2. Установка расширения

SQL UTF-8 Открыть файл
1
2
-- Выполнить от суперпользователя postgres
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

2.3. Параметры в postgresql.conf

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Подключение расширения к жизненному циклу сервера
shared_preload_libraries = 'pg_stat_statements'

# Максимальное количество уникальных нормализованных запросов в кэше.
# Для 1С с глубокой кастомизацией значение по умолчанию (5000) может
# переполниться - увеличиваем до 10000.
pg_stat_statements.max = 10000

# Учитывать вложенные запросы внутри функций (по умолчанию - только верхний уровень, top).
pg_stat_statements.track = all

# Учитывать служебные команды (DDL, COPY, VACUUM).
pg_stat_statements.track_utility = on

# Собирать время чтения и записи блоков: без него поля blk_read_time и
# blk_write_time в pg_stat_statements остаются нулями.
track_io_timing = on

# Сохранять статистику между перезапусками PostgreSQL.
pg_stat_statements.save = on

После изменения shared_preload_libraries требуется перезапуск PostgreSQL. Остальные параметры применяются командой SELECT pg_reload_conf(); без перезапуска. track_io_timing добавляет вызовы таймера на каждое чтение блока; на серверах с быстрыми часами это незаметно, проверить можно утилитой pg_test_timing.

2.4. Проверка корректности установки

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- Убедиться, что расширение загружено и видит данные
SELECT count(*) FROM pg_stat_statements;

-- Проверить топ-5 запросов по суммарному времени выполнения
SELECT
    left(query, 80)            AS query_preview,
    calls,
    round(total_exec_time::numeric, 2)  AS total_ms,
    round(mean_exec_time::numeric, 2)   AS mean_ms,
    round(stddev_exec_time::numeric, 2) AS stddev_ms,
    rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 5;

🔍 Установка и запуск pg_profile

pg_stat_statements показывает накопленную статистику с момента последнего сброса. Он не отвечает на вопрос «что именно тормозило с 10:00 до 11:30 в час пиковых отгрузок». Для временных срезов нужен pg_profile.

3.1. Что такое pg_profile

pg_profile - расширение PostgreSQL, которое с заданной периодичностью делает снимки (snapshot) представлений статистики (pg_stat_statements, pg_stat_bgwriter, pg_stat_user_tables, pg_statio_user_tables и других), сохраняет их в служебных таблицах и умеет строить дифференциальные HTML/текстовые отчеты за любой выбранный интервал между двумя снимками.

3.2. Установка

Bash UTF-8 CLI
1
2
3
4
5
6
7
# pg_profile - набор SQL-файлов. Берём релиз с GitHub (на стенде статьи - 4.16)
# и распаковываем в каталог расширений PostgreSQL:
curl -sLO https://github.com/zubkov-andrei/pg_profile/releases/download/4.16/pg_profile--4.16.tar.gz
sudo tar xzf pg_profile--4.16.tar.gz -C $(pg_config --sharedir)/extension

# pg_cron ставится пакетом (Debian/Ubuntu, PGDG):
sudo apt-get install postgresql-16-cron

Для pg_profile нужно расширение dblink из пакета postgresql-contrib. В официальном образе postgres:16 оно уже есть, на Debian и Ubuntu ставится отдельным пакетом.

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
-- Создать схему хранения снимков в отдельной служебной базе
-- (не в продуктивной базе 1С - это важно)
CREATE DATABASE pgprofile_repo;
\c pgprofile_repo
CREATE SCHEMA profile;
CREATE EXTENSION dblink;
CREATE EXTENSION pg_profile SCHEMA profile;

Важно: Хранилище снимков (pgprofile_repo) рекомендуется создавать в отдельной базе данных. Это исключает любое влияние аналитических таблиц на рабочие табличные пространства 1С.

3.3. Регистрация сервера и настройка расписания снимков

Сначала в базе 1С нужна роль наблюдателя. Имя pg_monitor для входа не подходит: имена с префиксом pg_ зарезервированы, а pg_monitor - встроенная роль без права входа. Создаём свою роль и выдаём ей pg_monitor:

SQL UTF-8 Открыть файл
1
2
3
4
-- В базе 1С (trade_db), от суперпользователя:
CREATE ROLE pgprofile_mon LOGIN PASSWORD '...';
GRANT pg_monitor TO pgprofile_mon;
GRANT EXECUTE ON FUNCTION pg_stat_statements_reset(oid, oid, bigint) TO pgprofile_mon;

Последняя строка обязательна: без неё первый снимок завершается ошибкой permission denied for function pg_stat_statements_reset. Для роли нужна строка в pg_hba.conf.

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- В базе pgprofile_repo: зарегистрировать продуктивный сервер 1С
SELECT profile.create_server(
    'торговый_контур',                        -- имя сервера в репозитории
    'host=127.0.0.1 port=5432 dbname=trade_db user=pgprofile_mon password=...'
);

-- Хранить снимки 30 дней (старые pg_profile удаляет сам при очередном снимке)
SELECT profile.set_server_max_sample_age('торговый_контур', 30);

-- Сделать первый снимок вручную (для проверки): ожидается (OK,...)
SELECT profile.take_sample('торговый_контур');

-- Убедиться, что снимок появился
SELECT * FROM profile.show_samples('торговый_контур');

Автоматизация снимков через pg_cron (рекомендуемый интервал - 30 минут для продуктивных контуров):

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
-- Установить pg_cron в postgresql.conf:
-- shared_preload_libraries = 'pg_stat_statements,pg_cron'
-- cron.database_name = 'pgprofile_repo'

-- После перезапуска PostgreSQL:
\c pgprofile_repo
CREATE EXTENSION pg_cron;

SELECT cron.schedule(
    'pg_profile_snapshot',
    '*/30 * * * *',   -- каждые 30 минут
    $$SELECT profile.take_sample('торговый_контур')$$
);

🔍 Методика чтения отчета pg_profile

4.1. Генерация отчета за инцидентный интервал

Допустим, дашборд Grafana зафиксировал всплеск TLOCKS с 10:15 до 11:45. Находим номера снимков, охватывающих этот период:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
\c pgprofile_repo

SELECT sample, sample_time
FROM profile.show_samples('торговый_контур')
WHERE sample_time BETWEEN '2026-09-05 10:00+03' AND '2026-09-05 12:00+03'
ORDER BY sample;
 sample |      sample_time
--------+------------------------
     42 | 2026-09-05 10:00:12+03
     43 | 2026-09-05 10:30:08+03
     44 | 2026-09-05 11:00:14+03
     45 | 2026-09-05 11:30:09+03
     46 | 2026-09-05 12:00:11+03

В таблице profile.samples имени сервера нет (там server_id), поэтому список снимков берём через show_samples. Номера в выводе - пример из кейса.

Генерируем отчёт за интервал снимков 42-46. Функция возвращает HTML одной строкой; чтобы файл открылся в браузере, сохраняем его в режиме «только данные, без выравнивания»:

Bash UTF-8 CLI
1
2
psql -U postgres -d pgprofile_repo -qAt -o /tmp/report_incident_20260905.html \
  -c "SELECT profile.get_report('торговый_контур', 42, 46)"

Номера снимков искать не обязательно: отчёт за интервал времени - profile.get_report('торговый_контур', tstzrange(now() - interval '2 hours', now())).

4.2. Навигация по разделам отчета

Названия разделов даны так, как они выглядят в отчёте pg_profile 4.16 (интерфейс английский), в порядке разбора:

Раздел отчёта На что смотрим в первую очередь
Top SQL by execution time Суммарное время выполнения. Кандидаты на оптимизацию - запросы с долей больше 10% от суммы всех.
Top SQL by shared blocks read Запросы, читающие больше всего блоков не из shared_buffers. Признак нехватки кэша или отсутствия индекса.
Top SQL by executions Очень частые, но «быстрые» запросы. Если их среднее время растёт - признак деградации плана или конкуренции за ресурсы.
Top SQL by shared blocks dirtied Запросы, создающие много грязных страниц. Прямое влияние на checkpoint.
Top tables by blocks read Какие таблицы читались с диска больше всего. Так находится физическая таблица PostgreSQL под регистром 1С.
Top tables by estimated sequentially scanned volume Таблицы, которые читаются последовательным сканированием. Для регистров с индексами это тревожный признак.
Top tables by vacuum operations, Top tables by modified tuples ratio Отстаёт ли autovacuum на высокооборотных таблицах.
Cluster settings Значения параметров на момент отчёта: проверить, что применён именно ваш конфиг.

Раздел с типами ожиданий (Lock, LWLock, IO) появляется в отчёте, только если на сервере установлено расширение pg_wait_sampling. На стенде статьи его нет, и раздела в отчёте нет. Без него ожидания смотрим запросами к pg_stat_activity из раздела 6.4.

4.3. Пример анализа реального инцидента

В отчете «Торгового контура» за инцидентный период раздел Top SQL by execution time показал:

Rank 1: total_time = 48 min 12 sec | calls = 41 230 | mean = 70 ms
Query ID: 3f7a2c...
SELECT ... FROM _AccumRg12345 t1 ...
WHERE t1._Period = $1 AND t1._Recorder = $2 ...

Запрос обращается к таблице _AccumRg12345 (регистр накопления «ТоварыНаСкладах»). 41 тысяча вызовов за 1.5 часа с средним временем 70 мс - при пиковой нагрузке среднее время отдельных вызовов достигало 8-14 секунд, что и фиксировал ТЖ.

Раздел Top tables by blocks read подтвердил: _AccumRg12345 - лидер по heap_blks_read (чтение с диска), при этом heap_blks_hit (из shared_buffers) было на порядок ниже. Прямой сигнал: таблица не помещается в буферный кэш или планировщик обходит нужный индекс.

🔍 Связка ClickHouse ↔ PostgreSQL: от SDBL-события до плана запроса

Ценность конвейера из Части 1 - в том, что текст SQL-запроса хранится в ClickHouse в поле sql событий SDBL. Это позволяет построить сквозной маршрут расследования:

Рис. 2. Путь расследования от всплеска на дашборде до плана запроса

5.1. Запрос к ClickHouse для извлечения SQL-текста

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
-- В интерфейсе Grafana / ClickHouse клиент
SELECT
    timestamp,
    round(duration / 1000000, 2) AS duration_sec,
    user,
    substring(sql, 1, 300)       AS sql_preview,
    context
FROM techlog.events
WHERE event = 'SDBL'
  AND duration > 5000000   -- дольше 5 секунд
  AND toDate(timestamp) = '2026-09-05'
ORDER BY duration DESC
LIMIT 20;

5.2. Поиск запроса в pg_stat_statements

Текст SQL из ТЖ 1С содержит конкретные значения параметров ($1 = '2026-09-05', $2 = ...). pg_stat_statements хранит нормализованные тексты - с плейсхолдерами $1, $2. Для поиска используем ключевые фрагменты структуры запроса:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
SELECT
    queryid,
    left(query, 200)     AS query_preview,
    calls,
    round(mean_exec_time::numeric, 2) AS mean_ms,
    round(total_exec_time::numeric / 60000, 2) AS total_min
FROM pg_stat_statements
WHERE query ILIKE '%_AccumRg12345%'
  AND query ILIKE '%_Period%'
ORDER BY total_exec_time DESC
LIMIT 5;

5.3. EXPLAIN ANALYZE на тестовой реплике

Никогда не запускайте EXPLAIN ANALYZE на продуктивной базе с реальными данными в рабочее время - это выполнит запрос фактически и добавит нагрузку. Используйте потоковую реплику только для чтения:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
-- На реплике (read-only):
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT t1."_Period", sum(t1."_Fld567")
FROM "_AccumRg12345" t1
WHERE t1."_Period" = '2026-09-05'
  AND t1."_Recorder" = '\x...'
GROUP BY t1."_Period";

Пример вывода, указывающего на проблему:

Seq Scan on "_AccumRg12345"  (cost=0.00..1842310.00 rows=1 width=...)
                             (actual time=8420.113..8420.115 rows=1 loops=1)
  Filter: (("_Period" = ...) AND ("_Recorder" = ...))
  Rows Removed by Filter: 598241037
  Buffers: shared hit=12450 read=229871
Planning Time: 0.8 ms
Execution Time: 8421.2 ms

Seq Scan при Rows Removed by Filter: 598 миллионов - планировщик выбрал полный перебор таблицы вместо индекса. Причина выясняется в следующем разделе.

🔍 Типовые паттерны неоптимальных планов для запросов 1С

6.1. Seq Scan на регистре накопления из-за устаревшей статистики

Симптом: Планировщик выбирает Seq Scan на таблице регистра накопления несмотря на наличие составного индекса по (_Period, _Recorder, _LineNo). Имена полей здесь и в примерах выше условные: в базе 1С вместо _Recorder стоят поля _RecorderTRef и _RecorderRRef, а номера в именах _AccumRg12345 и _Fld567 у каждой конфигурации свои.

Причина: Статистика таблицы (pg_statistic) устарела. После массовой перепроводки документов реализации (типовая ночная операция) или большого импорта данных через API распределение значений в столбце _Period радикально изменилось. AUTOVACUUM не успел обновить статистику до начала рабочего дня. Планировщик оценивает селективность фильтра по _Period как слишком низкую и считает Seq Scan дешевле Index Scan.

Решение:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
-- Принудительно обновить статистику таблицы
-- (безопасно, выполняется параллельно с работой пользователей)
ANALYZE "_AccumRg12345";

-- Для регулярного контроля - настроить более агрессивный autovacuum
-- на таблицах с высокой скоростью изменений:
ALTER TABLE "_AccumRg12345"
    SET (autovacuum_analyze_scale_factor = 0.01,   -- анализировать при 1% изменений
         autovacuum_analyze_threshold = 1000);

6.2. Nested Loop на JOIN временных таблиц 1С

Симптом: Запрос с JOIN временной таблицы #tt... и большого регистра выбирает Nested Loop вместо Hash Join. При большом количестве строк во временной таблице время растет квадратично.

Причина: Статистика временных таблиц в PostgreSQL не собирается автоматически. Планировщик не знает реальный размер #tt... и недооценивает стоимость Nested Loop.

Решение в контексте 1С: Платформа создает временные таблицы и управляет их жизненным циклом, поэтому прямое влияние ограничено. Проверьте в EXPLAIN (ANALYZE, BUFFERS), что хеш-таблица не сбрасывается на диск (в узле Hash значение Batches больше 1): work_mem не заставляет планировщик выбирать Hash Join, он определяет лишь, поместится ли хеш-таблица в памяти.

Ini UTF-8 Открыть файл
1
work_mem = '64MB'   # см. раздел 7

6.3. Index Scan вместо Index Only Scan из-за мертвых строк (bloat)

Симптом: Запрос по индексу выполняется медленнее ожидаемого. EXPLAIN BUFFERS показывает высокий heap fetches при Index Scan.

Причина: Таблица накопила мертвые строки (tuple bloat) из-за задержки VACUUM. Для проверки видимости строки PostgreSQL вынужден обращаться к heap даже при Index Scan - теряется преимущество Index Only Scan.

Диагностика:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
-- Найти таблицы с большим числом мертвых строк
SELECT
    schemaname,
    relname,
    n_dead_tup,
    n_live_tup,
    round(100.0 * n_dead_tup / nullif(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
    last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 100000
ORDER BY dead_pct DESC
LIMIT 10;

Решение:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
-- VACUUM ANALYZE не блокирует чтение и запись в таблице
VACUUM ANALYZE "_AccumRg12345";

-- Для таблиц с высоким оборотом - ускорить autovacuum:
ALTER TABLE "_AccumRg12345"
    SET (autovacuum_vacuum_scale_factor = 0.02,
         autovacuum_vacuum_threshold = 500,
         autovacuum_vacuum_cost_delay = 2);  -- мс, снизить тормозящий I/O

6.4. Lock wait на уровне строк при параллельной маркировке

Симптом: В pg_stat_activity много сессий с wait_event_type = 'Lock', а wait_event равен transactionid или tuple. Время ожидания коррелирует с потоком API-вызовов маркировки.

Причина: Транзакции проведения документов реализации и фоновые процессы верификации кодов маркировки меняют одни и те же строки таблицы _AccumRg... (регистр остатков). Пока первая транзакция не завершилась, вторая ждёт снятия блокировки строки. RowExclusiveLock тут ни при чём: это блокировка таблицы, которую берёт любой INSERT и UPDATE, и с себе подобными она не конфликтует. Ожидание вызывают блокировки строк, поэтому искать надо transactionid и tuple.

Диагностика во время инцидента:

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- Текущие блокировки: кто кого блокирует
SELECT
    blocked.pid        AS blocked_pid,
    blocked.usename    AS blocked_user,
    blocking.pid       AS blocking_pid,
    blocking.usename   AS blocking_user,
    blocked.wait_event_type,
    blocked.wait_event,
    left(blocked.query, 100) AS blocked_query,
    left(blocking.query, 100) AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
    ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;

Решение (архитектурное, реализуется в Части 6А): Вынос потока API-верификации маркировки в асинхронный контур через RabbitMQ. Это ликвидирует прямую конкуренцию транзакций в СУБД.

🔍 Тюнинг postgresql.conf под OLTP-нагрузку 1С

Все изменения вносятся в postgresql.conf (или conf.d/1c_tuning.conf) и применяются командой SELECT pg_reload_conf();. Параметры, требующие перезапуска, помечены [restart].

7.1. Буферный кэш (shared_buffers)

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
# Стандартная рекомендация: 25% RAM.
# На сервере с 128 ГБ RAM:
shared_buffers = 32GB     # [restart]

# Эффективный размер кэша файловой системы (подсказка планировщику).
# Устанавливать в 50-75% RAM:
effective_cache_size = 80GB

Проверка эффективности буферного кэша:

SQL UTF-8 Открыть файл
1
2
3
4
5
SELECT
    sum(heap_blks_hit)::float /
    nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) AS buffer_hit_ratio
FROM pg_statio_user_tables;
-- Целевое значение: > 0.95 (95% чтений из кэша, не с диска)

Если buffer_hit_ratio ниже 0.90 на нагруженных таблицах регистров - увеличить shared_buffers.

7.2. Рабочая память для сортировок и хеш-таблиц (work_mem)

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
8
# Память для одной операции сортировки/хеш-соединения.
# Критически важно для временных таблиц 1С и GROUP BY на больших регистрах.
#
# Внимание: этот параметр - на одну операцию, а не на соединение.
# При 350 пользователях и сложных запросах с несколькими Sort-узлами
# реальное потребление может быть: 350 * 5 operations * 64MB = около 110 ГБ
# Устанавливать осторожно, контролируя через pg_stat_activity.
work_mem = 64MB

7.3. Сглаживание I/O при checkpoint

Агрессивные checkpoint-ы (сброс грязных страниц на диск) создают I/O-шипы, которые прямо конкурируют с пользовательскими транзакциями.

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
# Размер WAL между checkpoint-ами.
# По умолчанию 1GB - слишком мало для базы 1.8 ТБ с высоким потоком изменений.
max_wal_size = 8GB

# Минимальный размер WAL (предотвращает частые маленькие checkpoint-ы):
min_wal_size = 2GB

# Растянуть сброс страниц на 90% интервала между checkpoint-ами.
# В PostgreSQL 14 и новее 0.9 - значение по умолчанию, в 13 и старше его нужно задать:
checkpoint_completion_target = 0.9

7.4. Стоимостная модель планировщика для SSD

По умолчанию PostgreSQL настроен на HDD (random_page_cost = 4.0). На SSD/NVMe случайный и последовательный доступ практически равноценны.

Ini UTF-8 Открыть файл
1
2
3
4
5
6
# Для SSD/NVMe:
random_page_cost = 1.1
seq_page_cost = 1.0

# Число одновременных запросов предвыборки страниц (bitmap heap scan). По умолчанию 1:
effective_io_concurrency = 200   # для SSD, 1-2 для HDD

Снижение random_page_cost позволяет планировщику чаще выбирать Index Scan вместо Seq Scan - это напрямую решило проблему с регистром накопления из примера выше.

7.5. Логирование медленных запросов на уровне СУБД

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
# Логировать запросы, выполнявшиеся дольше 3 секунд.
# Дополняет ТЖ 1С: здесь будут запросы от сервисных соединений и системных процессов.
log_min_duration_statement = 3000   # мс

# Логировать план выполнения для медленных запросов (модуль auto_explain,
# его нужно добавить в shared_preload_libraries или session_preload_libraries):
# auto_explain.log_min_duration = 3000
# auto_explain.log_analyze = on
# auto_explain.log_buffers = on

7.6. Итоговый конфигурационный файл

Ini UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# /etc/postgresql/16/main/conf.d/1c_tuning.conf
# Торговый контур: 128 ГБ RAM, NVMe SSD, PostgreSQL 16

# ^72;^72;^72; Память ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
shared_buffers                  = 32GB
effective_cache_size            = 80GB
work_mem                        = 64MB
maintenance_work_mem            = 2GB

# ^72;^72;^72; Checkpoint & WAL ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
max_wal_size                    = 8GB
min_wal_size                    = 2GB
checkpoint_completion_target    = 0.9
wal_buffers                     = 64MB

# ^72;^72;^72; Планировщик (для SSD/NVMe) ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
random_page_cost                = 1.1
seq_page_cost                   = 1.0
effective_io_concurrency        = 200
default_statistics_target       = 200   # точнее статистика → лучше планы

# ^72;^72;^72; Autovacuum (агрессивный для высокооборотных таблиц) ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
autovacuum_vacuum_scale_factor  = 0.05
autovacuum_analyze_scale_factor = 0.02
autovacuum_max_workers          = 5
autovacuum_vacuum_cost_delay    = 2ms

# ^72;^72;^72; Параллелизм ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
max_parallel_workers_per_gather = 4
max_parallel_workers            = 8

# ^72;^72;^72; Логирование ^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;
track_io_timing                 = on
log_min_duration_statement      = 3000
log_line_prefix                 = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_lock_waits                  = on
deadlock_timeout                = 1s

🔍 Верификация и метрики

Применение изменений в конфигурации PostgreSQL верифицируется в контексте кейса «Торговый контур» по следующим метрикам.

Метрика До После Источник
Время проведения документа реализации (p95) 14.2 с 1.8 с ClickHouse: агрегация SDBL по duration
Buffer hit ratio (_AccumRg12345) 82% 98% pg_statio_user_tables
I/O-spike в момент checkpoint +340 IOPS +65 IOPS Node Exporter: node_disk_io_time_seconds_total
Количество Seq Scan на регистре за сутки 1 247 3 pg_stat_user_tables.seq_scan
MTTR при инцидентах СУБД 45-90 мин 8 мин pg_profile: отчет готов за 2 минуты, диагноз очевиден

8.1. Запрос для мониторинга эффективности индексов

Добавьте в дашборд Grafana через Prometheus / postgres_exporter или через прямой запрос к ClickHouse (если экспортируете метрики):

SQL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- Таблицы с высоким seq_scan - кандидаты на добавление индексов
-- или принудительный ANALYZE
SELECT
    schemaname || '.' || relname      AS table_name,
    seq_scan,
    idx_scan,
    n_live_tup,
    round(seq_scan::numeric /
        nullif(seq_scan + idx_scan, 0) * 100, 1) AS seq_pct
FROM pg_stat_user_tables
WHERE seq_scan + idx_scan > 100
  AND n_live_tup > 100000
ORDER BY seq_scan DESC
LIMIT 20;

🔍 Риски и ограничения

Риск 1: Завышенный work_mem вызывает OOM-killer.

При одновременном выполнении сложных аналитических запросов (например, закрытие месяца или расчет себестоимости) несколько параллельных сортировок могут одновременно выделить N_сессий × work_mem. Если сумма превышает доступную RAM, ядро Linux убивает процессы.

Митигация: Устанавливать work_mem консервативно (32-64 МБ для OLTP). Отдельно повышать work_mem для конкретных аналитических сессий:

SQL UTF-8 Открыть файл
1
SET work_mem = '512MB';  -- только для текущей сессии закрытия месяца

Риск 2: VACUUM ANALYZE на больших таблицах в рабочее время.

На таблице в 600 миллионов строк VACUUM может выполняться 20-40 минут. В это время процесс удерживает блокировку SHARE UPDATE EXCLUSIVE, которая мешает конкурентным DDL-операциям (ALTER TABLE, CREATE INDEX без CONCURRENTLY). На DML (INSERT/UPDATE/DELETE) это не влияет.

Митигация: Запускать принудительный VACUUM только вне пиковых часов либо использовать VACUUM (PARALLEL 4): параллельно обрабатываются только индексы таблицы.

Риск 3: pg_profile занимает место в служебной базе.

При снимках каждые 30 минут и хранении 30 дней объем репозитория для базы с 5 000 уникальных запросов - порядка 2-5 ГБ. Срок хранения задаётся один раз, старые снимки pg_profile удаляет сам при очередном снимке:

SQL UTF-8 Открыть файл
1
SELECT profile.set_server_max_sample_age('торговый_контур', 30);  -- дней

🔍 Артефакты для читателя

К статье прилагается архив release_part_02_postgresql.zip:

  • scripts/1c_tuning.conf - конфигурационный файл с комментариями под базы 1С.
  • scripts/setup_extensions.sql - установка pg_stat_statements, pg_profile, pg_cron, роль наблюдателя, регистрация сервера и расписание снимков.
  • scripts/diagnostic_queries.sql - семь блоков диагностических запросов: буферный кэш, медленные запросы, индексы и Seq Scan, мёртвые строки, блокировки, checkpoint, суточная сводка.
  • scripts/pgprofile_report.sql - просмотр снимков, отчёты, управление хранением.
  • scripts/README.md - быстрый старт и контрольный чеклист.

Как проверено. Четыре SQL-файла и конфигурационный файл прогнаны на чистом PostgreSQL 16.15 (официальный образ postgres:16, Docker) с pg_profile 4.16 и pg_cron из пакета postgresql-16-cron: сервер стартует с 1c_tuning.conf, снимок создаётся (OK), отчёт сохраняется в HTML размером около 500 КБ, диагностические запросы выполняются без ошибок. Прогон повторяется скриптом evidence/run_scripts.sh из репозитория работы. Память на стенде уменьшена: 256 МБ shared_buffers вместо 32 ГБ. Числа кейса «Торговый контур» (таблица в разделе 8) этим прогоном не проверяются.

🔍 Заключение

Практические результаты

  • Выстроена сквозная цепочка расследования инцидентов: от события SDBL в ClickHouse до конкретного плана выполнения запроса в PostgreSQL.
  • Устранен Seq Scan на регистре накопления «ТоварыНаСкладах»: время проведения документов реализации (p95) снизилось с 14.2 с до 1.8 с.
  • Налажен регулярный сбор снимков pg_profile: MTTR при инцидентах уровня СУБД сократился до 8 минут.
  • Конфигурация PostgreSQL приведена в соответствие с реальным профилем нагрузки: SSD, OLTP, 350 пользователей.

Переход к следующей части

Теперь СУБД настроена, планы запросов оптимальны, а статистика собирается автоматически. Но в часы пиковых отгрузок инженер по-прежнему видит на Grafana периодические всплески потребления RAM процессами rphost, которые заканчиваются превентивными ночными рестартами.

В Части 3. Управление памятью rphost: jemalloc, cgroups v2 и safe-ротация без остановки бизнеса мы перейдем на уровень рантайма платформы 1С: настроим аллокатор памяти jemalloc, ограничим потребление ресурсов через cgroups v2 без риска OOM-kill в рабочее время и реализуем безопасный механизм плавной ротации рабочих процессов для обоих сценариев - КОРП и ПРОФ.

Кейс «Торговый контур» - на следующих конфигурациях и релизах:

  • 1С:ERP Управление предприятием 2, релизы 2.6.1.53
  • 1С:Комплексная автоматизация 2.х, релизы 2.7.4.25
  • PostgreSQL 16.4, Ubuntu 22.04 LTS

Скрипты архива дополнительно проверены на PostgreSQL 16.15 с pg_profile 4.16 (Docker).

Вступайте в нашу телеграмм-группу Инфостарт

Вступайте в нашу телеграмм-группу Инфостарт

PostgreSQL pg_profile pg_stat_statements производительность 1С postgresql.conf Linux

См. также

Работа с интерфейсом Анализ учета Мониторинг 1С:Предприятие 8 1С 8.3 1C:Бухгалтерия 1С:Бухгалтерия 3.0 1С:ERP Управление предприятием 2 1С:Управление холдингом 1С:Зарплата и Управление Персоналом 3.x 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Управление торговлей 11 Платные (руб)

Создайте свой функциональный интерфейс в любой конфигурации 1С с помощью расширения Infostart Dashboard. Настраивайте панели виджетов с метриками, индикаторами и показателями на начальном экране. Узнайте возможность внедрения подсистемы у себя в конфигурации с помощью бесплатной обработки "Анализ внедрения подсистемы 1С Infostart Dashboard"!

31720 руб.

27.03.2025    90963    66    44    

77

Инструменты администратора БД Корректировка данных Мониторинг Учет документов 1С 8.3 1С:Управление торговлей 10 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Розница 3.0 Платные (руб)

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

6100 руб.

11.06.2026    882    2    0    

4

DevOps и автоматизация разработки Мониторинг Тестирование QA Разработчик 1С:Предприятие 8 Бесплатно (free)

Платформа 1С давно вышла за рамки учетных систем. Сегодня это полноценная среда для создания сложных, высоконагруженных и распределенных приложений. А значит, и стек технологий современного разработчика кардинально изменился. Систематизируем весь инструментарий, который превращает 1С-программиста в инженера: от EDT и Git до автотестов на YAxUnit, контейнеризации приложений в Docker, мониторинга в Prometheus и организации шины данных на Kafka. Разберемся, зачем каждый инструмент нужен, как он вписывается в жизненный цикл разработки и с чего начать его внедрение.

25.08.2026    22734    mrXoxot    55    

84

HighLoad оптимизация Разработчик 1С:Предприятие 8 1C:ERP Бесплатно (free)

Приведем примеры использования различных в динамических списках и посмотрим, почему это плохо.

18.02.2025    16131    ivanov660    39    

62

Логистика, склад и ТМЦ Мониторинг Маркетплейсы Пользователь 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Платные (руб)

Расширение для 1С, которое автоматически «отлавливает» тарифы складов с наиболее выгодными коэффициентами для ваших товаров на маркетплейсе Wildberries. С помощью этого инструмента вы сможете легко находить и выбирать склады с лучшими условиями для максимизации своей прибыли. Удобная интеграция позволяет настроить регулярный поиск складов по выгодным коэффициентам в виде регламентного задания в 1С, что существенно экономит время и автоматизирует процесс принятия решений по размещению товаров. Всегда будьте на шаг впереди конкурентов и повышайте эффективность своего бизнеса с помощью «Ловца коэффициентов складов Wildberries»!

6100 руб.

14.11.2024    2549    1    0    

4

Мониторинг Анализ продаж 1С:Предприятие 8 1C:Бухгалтерия 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Розница 3.0 Управленческий учет Платные (руб)

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

24400 руб.

11.11.2024    3227    1    0    

2

Учет доходов и расходов Логистика, склад и ТМЦ Маркетплейсы Мониторинг Пользователь 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Управленческий учет Платные (руб)

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

5000 руб.

31.10.2024    2557    1    0    

3

HighLoad оптимизация Технологический журнал Системный администратор Разработчик Бесплатно (free)

Обсудим поиск и разбор причин длительных серверных вызовов CALL, SCALL.

24.06.2024    18751    ivanov660    13    

64
Для отправки сообщения требуется регистрация/авторизация