Как устранить медленную запись в регистры бухгалтерии 1С после обновления PostgreSQL до версии 16/17?

Системный администратор 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

Переход со старых версий PostgreSQL (например, 10-й) на современные (16-ю или 17-ю) в связке с платформой 1С:Предприятие 8.3 часто обещает значительный прирост производительности. Однако на практике системные администраторы нередко сталкиваются с обратным эффектом: операции проведения документов, которые раньше выполнялись мгновенно, начинают занимать десятки секунд. Особенно остро эта проблема проявляется в таблицах регистров бухгалтерии (_AccRg).

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

Анализируем аномалии в планах выполнения запросов

Первым делом нам необходимо выяснить, на что именно PostgreSQL тратит время. Проанализируем типичный план запроса SELECT к таблице регистра бухгалтерии, который в новой версии выполняется за 35-40 мс вместо привычных 1 мс.


EXPLAIN (ANALYZE, BUFFERS) 
SELECT * FROM _AccRg12303 
WHERE _Period > '2025-01-01' LIMIT 100;

Limit (actual time=35.878..36.209 rows=100 loops=1)
  Buffers: shared hit=3494 read=24
  -> Index Scan using _accrg12303_10 on _accrg12303 (cost=0.42..27251.54 rows=47497 width=464)

Обратим внимание на показатель Buffers: shared hit=3494. Это означает, что для получения всего 100 строк PostgreSQL пришлось прочитать из кэша памяти 3494 страницы (каждая по 8 КБ), что суммарно составляет около 27.5 МБ данных. Рассмотрим, почему это критично:

  1. Несоответствие физического порядка: В PostgreSQL 16 планировщик может ошибочно полагать, что данные в индексе расположены последовательно. В реальности нужные 100 строк могут быть «размазаны» по всей таблице.
  2. Проверка видимости (MVCC): Если в таблице много «мусорных» строк (dead tuples), Postgres тратит время на их чтение из памяти, чтобы затем отсечь, так как они невидимы для текущей транзакции.
  3. Чтение всей строки (Heap Fetch): Поскольку выполняется SELECT *, СУБД не может ограничиться данными только из индекса (Index Only Scan) и вынуждена для каждой строки обращаться к основной таблице (Heap).

Проблема изменения кластерного индекса при миграции

При переносе базы данных через pg_dump и pg_restore может произойти скрытая подмена основного индекса, по которому кластеризована таблица. Проанализируем структуру индексов на старом и новом серверах. Выяснится, что на PostgreSQL 10 использовался индекс _accrg12303_1, а на PostgreSQL 16 — _accrg12303_10, который включает дополнительные поля.

Это приводит к тому, что физический порядок записей на диске перестает соответствовать порядку, в котором 1С запрашивает данные. Чтобы исправить это, выполним принудительную перегруппировку данных:


-- Указываем СУБД, какой индекс использовать для физического упорядочивания
CLUSTER _AccRg12303 USING _accrg12303_1;

-- Обновляем статистику для планировщика
ANALYZE _AccRg12303;

-- Прогреваем кэш, чтобы исключить влияние дисковой подсистемы
SELECT pg_prewarm('_accrg12303');

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

Оптимизация параметров стоимости для NVMe-дисков

Современные версии PostgreSQL по умолчанию настроены на работу с медленными HDD-дисками. Параметр random_page_cost (стоимость произвольного чтения) по умолчанию равен 4.0, в то время как seq_page_cost (последовательное чтение) равен 1.0. Для быстрых NVMe-накопителей такая разница лишена смысла.

Разберем, как изменить настройки в postgresql.conf, чтобы планировщик перестал «бояться» индексного сканирования:

  1. Установим random_page_cost = 1.1. Это заставит планировщик чаще выбирать индексы, понимая, что прыжки по блокам данных на NVMe стоят почти столько же, сколько последовательное чтение.
  2. Проверим параметр effective_io_concurrency. Для NVMe его можно смело увеличивать до 200-300, что позволит СУБД лучше использовать параллелизм дисковой подсистемы.

Влияние виртуализации Hyper-V на производительность СУБД

Если ваш сервер PostgreSQL запущен внутри виртуальной машины под управлением Hyper-V, причиной замедления могут быть микрозадержки при системных вызовах времени. В PostgreSQL 12 и выше механизмы блокировок и транзакций чаще обращаются к системным часам.

Проверим текущий источник времени в Linux командой:

cat /sys/devices/system/clocksource/clocksource0/current_clocksource

Если вы видите значение hpet или acpi_pm, это может замедлять каждый шаг выполнения запроса на миллисекунды. Для Hyper-V крайне рекомендуется использовать tsc. Настройка производится через параметры загрузки ядра (GRUB):

GRUB_CMDLINE_LINUX_DEFAULT="... clocksource=tsc"

Настройка для инстансов с большим количеством баз данных

Ситуация, когда на одном сервере PostgreSQL размещено 460 баз данных (как в рассматриваемом случае), создает колоссальную нагрузку на разделяемую память (Shared Buffers) и системный каталог метаданных. В новых версиях PostgreSQL механизмы работы с блокировками в памяти стали более чувствительны к количеству объектов.

Рассмотрим рекомендации для такой конфигурации:

  1. Использование Huge Pages: Обязательно настройте Huge Pages в операционной системе. Это уменьшит накладные расходы процессора на управление таблицами страниц памяти при больших значениях shared_buffers.
  2. Разделение на кластеры: Вместо одного инстанса PostgreSQL с 460 базами, эффективнее поднять 3-4 инстанса на разных портах. Это позволит каждой группе баз иметь свои системные процессы и уменьшит конкуренцию за LWLocks (легковесные блокировки).
  3. JIT-компиляция: В PostgreSQL 16 включенный по умолчанию JIT (Just-In-Time compilation) может тратить больше времени на компиляцию коротких запросов 1С, чем на само выполнение. Попробуем отключить его глобально: jit = off

Особенности платформы 1С и обновления статистики

При использовании платформы 8.3.24+ важно убедиться, что модули online_analyze и plantuner (входящие в сборки от 1С или Postgres Pro) работают корректно. После массовой загрузки данных или реструктуризации статистика может не обновиться мгновенно, что приведет к выбору ошибочного плана Sequential Scan вместо Index Scan.

Проанализируем ситуацию с записью: если DELETE или INSERT в таблицу _AccRg занимают более 2 секунд, это явный признак того, что СУБД не использует индекс для поиска существующих записей. В этом случае помогает не просто реструктуризация, а полное удаление и создание индексов заново, либо выполнение VACUUM (ANALYZE, FREEZE).

Подводя итог, можно сказать, что замедление в 200 раз — это результат каскадного эффекта: неоптимальные индексы после миграции, завышенная стоимость чтения для планировщика и накладные расходы виртуализации. Последовательное применение описанных выше настроек позволяет вернуть производительность к показателям PostgreSQL 10 и даже превзойти их.

← На главную