При работе 1С с СУБД PostgreSQL системные администраторы и разработчики часто сталкиваются с ситуацией, когда после массового изменения данных (например, переоценки 10 000 позиций номенклатуры) типовые отчеты или обработки выгрузки начинают работать крайне медленно. В этой статье мы разберем, почему «простая» реиндексация не всегда помогает, как архитектура PostgreSQL влияет на скорость запросов 1С и что нужно изменить в коде и настройках сервера, чтобы вернуть производительность.
Первым делом проанализируем программный код. В ходе обсуждения выяснилось, что основной причиной катастрофического падения скорости стал классический антипаттерн разработки — выполнение запроса к базе данных внутри цикла. Рассмотрим, как это выглядит в коде:
// Неправильный подход, приводящий к тормозам
Для Каждого СтрокаТовара Из ТаблицаВыгрузки Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура) КАК ЦеныНоменклатурыСрезПоследних";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", СтрокаТовара.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
// Обработка результата...
КонецЦикла;
Разберем, почему это критично для PostgreSQL. В отличие от других СУБД, PostgreSQL несет существенные накладные расходы на каждое установление соединения и парсинг запроса. Когда мы выполняем 10 000 отдельных запросов, сервер тратит больше времени на обслуживание самого процесса вызова, чем на поиск данных.
Решение: всегда переписывайте такие участки кода на один пакетный запрос. Мы должны сначала подготовить список всей нужной номенклатуры, передать его в запрос как массив и получить все цены одним махом, после чего сопоставить их в памяти 1С.
// Правильный подход: один запрос на все позиции
МассивНоменклатуры = ТаблицаВыгрузки.ВыгрузитьКолонку("Номенклатура");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
| ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура В (&МассивНоменклатуры)) КАК ЦеныНоменклатурыСрезПоследних";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// Дальше работаем с таблицей значений в памяти
Выясним причину, по которой проблема обостряется именно после массовой переоценки. PostgreSQL использует механизм многоверсионности (MVCC). Когда 1С обновляет строку в регистре сведений (или удаляет и записывает новую), старая версия строки не удаляется физически. Она помечается как «мертвая» (dead tuple).
При переоценке 10 000 товаров в базе появляется 10 000 «мертвых» записей. Если такие операции проводятся часто, физический размер таблицы и ее индексов начинает расти («пухнуть»), хотя логическое количество записей остается прежним. Индексы становятся «дырявыми», и планировщик PostgreSQL при выполнении запроса вынужден считывать гораздо больше страниц с диска.
Именно поэтому в рассматриваемой ситуации помог «перелив» базы через бэкап: команда pg_dump и последующее восстановление физически пересоздают таблицы без лишнего мусора.
Чтобы не прибегать к радикальным мерам вроде удаления кластера, необходимо правильно использовать встроенные инструменты обслуживания. Рассмотрим разницу между ключевыми командами:
Для баз 1С рекомендуется запускать их совместно. Команда VACUUM ANALYZE выполняет очистку и сразу обновляет статистику. Если тормозит конкретная таблица (регистр), можно запустить команду для нее:
VACUUM ANALYZE имя_таблицы_субд;
Для определения имени таблицы в СУБД из 1С используйте функцию ПолучитьСтруктуруХраненияБазыДанных(). Это позволит вам точно знать, какой объект в PostgreSQL соответствует вашему регистру сведений.
Проанализируем ситуацию с ресурсами. Если ваша база данных расположена на SSD (как у автора вопроса), стандартные настройки PostgreSQL могут работать против вас.
Посмотрите на параметр random_page_cost в файле postgresql.conf. По умолчанию он равен 4.0. Это значение рассчитано на старые HDD, где случайное чтение обходится дорого. Для SSD этот параметр следует установить в значение 1.1 или 1.0.
Если оставить 4.0, PostgreSQL будет «думать», что использовать индекс (который требует случайного доступа к страницам) — это очень дорогое удовольствие. В итоге он может выбрать Sequential Scan (полное сканирование таблицы) там, где индекс отработал бы мгновенно. Это одна из частых причин внезапных тормозов после изменения объема данных.
Чтобы не запускать VACUUM вручную каждую ночь, настроим автоматическую очистку более агрессивно. Стандартные настройки запускают автовакуум, когда изменяется 20% данных в таблице. Для регистра с миллионами записей 20% — это слишком много.
Рекомендуем изменить следующие параметры в настройках сервера:
Если вы столкнулись с резким замедлением запросов к регистру в PostgreSQL, выполните следующие шаги:
ANALYZE для проблемной таблицы. Это заставит СУБД перестроить план запроса.VACUUM для удаления «мертвых» строк, возникших после массовой переоценки.Помните, что PostgreSQL требует более внимательного отношения к регламентному обслуживанию, чем MS SQL Server, но при правильной настройке и оптимизации кода 1С он показывает отличную производительность даже на больших объемах данных.