Как решить проблему высокой нагрузки на процессор SQL-сервера при частых запросах к таблице Config в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый администратор СУБД и разработчик 1С рано или поздно сталкивается с ситуацией, когда процессор сервера базы данных загружен под 100%, а в топе самых «тяжелых» запросов оказывается системный запрос чтения конфигурации. На первый взгляд ситуация выглядит парадоксально: пользователи выполняют повседневные операции (проводят документы, строят отчеты), а СУБД тратит до половины своего времени на чтение служебной таблицы Config.

В этой статье мы подробно разберем, почему возникает аномальная нагрузка от запроса SELECT Creation, Modified, Attributes, DataSize, BinaryData FROM Config WHERE FileName = P1 ORDER BY PartNo, как эта техническая проблема напрямую связана с прикладным кодом, переполненными регистрами сведений и ошибками компиляции модулей, а также по шагам пройдем путь локализации и устранения этой неисправности.

Суть проблемы: почему СУБД постоянно читает Config

Для начала давайте выясним причину, по которой платформа 1С обращается к таблице Config. В этой таблице хранятся все метаданные нашей информационной базы: структура конфигурации, формы, макеты, программный код общих модулей, модулей объектов и менеджеров. Поле BinaryData содержит непосредственно сжатые бинарные данные конфигурации. Так как размер отдельных файлов метаданных может превышать допустимый размер одной записи в СУБД, платформа разбивает их на части. Именно поэтому в запросе присутствует сортировка ORDER BY PartNo — она гарантирует, что фрагменты бинарного файла будут собраны сервером приложений 1С в правильном порядке.

В штатном режиме работы чтение этой таблицы происходит только при старте рабочих процессов rphost, подключении новых пользователей или первом вызове тех или иных объектов метаданных. После прочтения эти данные кэшируются в оперативной памяти сервера приложений 1С. Повторные обращения к СУБД за кодом модулей или формами выполняться не должны.

Если же этот запрос стабильно держится в топе по потреблению ресурсов CPU СУБД, это означает одно: кэш сервера приложений постоянно сбрасывается или не успевает накапливаться. Сервер приложений 1С вынужден непрерывно, тысячами раз в минуту, скачивать мегабайты бинарных данных конфигурации из SQL-сервера, заставляя последний тратить огромные вычислительные мощности на чтение дисков, распаковку LOB-данных (Large Object) и их передачу по сети.

Связующее звено: как переполненные регистры сведений вызывают лавину запросов к Config

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

  1. Наличие интеграций и фоновых заданий. В современных кастомных конфигурациях активно используются фоновые задания, HTTP-сервисы и механизмы обмена данными. Регистры сведений в таких схемах зачастую выполняют роль очередей сообщений или буферов обмена (например, регистрация изменений для обмена с мобильными клиентами, интеграция со станциями розлива, внешними сайтами и т.д.).
  2. Переполнение таблиц регистров. Если в работе внешней системы происходит сбой или принимающий узел перестает забирать данные, регистр сведений начинает лавинообразно заполняться миллионами записей.
  3. Падение рабочих процессов rphost по памяти. При попытке обработать гигантский объем данных в одной транзакции или при обычной выборке без ограничения порций, рабочий процесс rphost исчерпывает выделенную ему оперативную память и аварийно завершается операционной системой (процесс падает по ошибке Out of Memory).
  4. Сброс кэша и повторный запуск. При падении рабочего процесса весь накопленный им кэш конфигурации уничтожается. Кластер 1С мгновенно перезапускает процесс или перенаправляет сессии пользователей на другой рабочий процесс.
  5. Массовая повторная инициализация. Вновь созданный процесс rphost обязан с нуля инициализировать окружение для каждого входящего сеанса. Он начинает массово считывать конфигурацию из таблицы Config СУБД, чтобы скомпилировать программный код общих модулей. Если фоновое задание-убийца запускается автоматически сразу после старта системы, процесс падает снова, образуя бесконечный цикл: «старт -> чтение Config -> компиляция -> падение от нехватки памяти -> старт». В результате SQL-сервер непрерывно загружен чтением Config на 90%.

Усугубляющий фактор: ошибки компиляции общих модулей

Ситуация становится критической, если в программном коде конфигурации допущены грубые ошибки компиляции. Рассмотрим реальный лог проверки конфигурации:


{ОбщийМодуль.ОбменДаннымиГлобальный.Модуль(27,3)}: Процедура или функция с указанным именем не определена (ПоказатьОповещениеПользователя)
        <<?>>ПоказатьОповещениеПользователя(...) (Проверка: Мобильное приложение-клиент)
{ОбщийМодуль._ИМЦ_СервисныйГлобальный.Модуль(97,53)}: Переменная не определена (УровеньЖурналаРегистрации)
        ЗаписьЖурналаРегистрации(..., <<?>>УровеньЖурналаРегистрации.Ошибка, ...) (Проверка: Мобильное приложение-сервер)

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

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

Пошаговое руководство по решению проблемы

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

Шаг 1. Анализ размера и структуры таблиц базы данных

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


SELECT 
    t.NAME AS TableName,
    p.rows AS RowCounts,
    (SUM(a.total_pages) * 8) / 1024 AS TotalSpaceMB,
    (SUM(a.used_pages) * 8) / 1024 AS UsedSpaceMB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.object_id = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
WHERE 
    t.NAME LIKE '%Config%' OR t.NAME LIKE '%InfoReg%'
GROUP BY 
    t.NAME, p.rows
ORDER BY 
    TotalSpaceMB DESC;

Если мы видим, что таблица Config имеет адекватный размер (в пределах 30–100 МБ для небольших кастомных баз), но при этом количество чтений к ней зашкаливает, значит, проблема действительно в постоянном сбросе кэша. Если же мы обнаружили регистры сведений с миллионами записей (десятки гигабайт данных), то мы нашли потенциальный источник падения процессов.

Шаг 2. Идентификация проблемного регистра и очистка данных

Если в процессе анализа СУБД были выявлены «раздутые» таблицы регистров сведений, необходимо выяснить, какие именно данные их переполняют. Часто в полях дат таких таблиц можно встретить аномальные значения со смещением на 2000 лет (например, 4017-12-11 вместо 2017-12-11). Это стандартный механизм платформы 1С для записи дат без часовых поясов или пустых дат в MS SQL Server.

Для устранения переполнения выполним следующие действия:

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

НаборЗаписей = РегистрыСведений.НашПроблемныйРегистр.СоздатьНаборЗаписей();
// Если регистр имеет измерения, устанавливаем необходимые отборы для очистки порциями
НаборЗаписей.Отбор.УзелОбмена.Установить(СсылкаНаУзел);
НаборЗаписей.Записать(Истина); // Записываем пустой набор для удаления записей по отбору

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

Шаг 3. Исправление синтаксических ошибок компиляции модулей

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

Конфигурация -> Проверка конфигурации -> Отметить все флаги проверки сред выполнения.

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


Процедура ВыполнитьОбменНаСервере() Экспорт
    
    // Выполняем логику обмена данными
    Попытка
        ОбработатьОчередьРегистра();
    Исключение
        ЗаписатьЛогОшибки(ОписаниеОшибки());
        
        // КРИТИЧЕСКИ ВАЖНО: Нельзя вызывать интерактивные методы на сервере!
        #Если Клиент Тогда
            ПоказатьОповещениеПользователя("Ошибка обмена данными!");
        #КонецЕсли
    КонецПопытки;
    
КонецПроцедуры

Использование директив #Если Клиент Тогда ... #КонецЕсли гарантирует, что сервер приложений 1С при компиляции этого общего модуля успешно проигнорирует клиентский метод ПоказатьОповещениеПользователя, модуль скомпилируется без ошибок и корректно сохранится в кэше сервера приложений.

Шаг 4. Устранение зависших динамических обновлений

Иногда бесконечное перечитывание таблицы Config связано с неудачно проведенным динамическим обновлением конфигурации базы данных (так называемым «демоническим» обновлением). При таком обновлении в служебной таблице Params СУБД может зависнуть запись с именем DynamicallyUpdated. Наличие этой записи заставляет платформу при любом запросе сомневаться в актуальности локального кэша и повторно перечитывать структуру метаданных.

Для исправления этой проблемы выполним следующие действия в нерабочее время:

  1. Завершим все активные сеансы пользователей и фоновые задания в консоли администрирования кластера 1С.
  2. В конфигураторе выполним команду: Конфигурация -> Информационная база -> Вернуться к конфигурации БД. Если команда активна, значит, динамическое обновление зависло, и его откат решит проблему.
  3. Если откат через конфигуратор недоступен, сделаем резервную копию базы данных СУБД и выполним очистку кэша динамического обновления напрямую через SQL-запрос:

-- Проверим наличие признака динамического обновления в служебной таблице
SELECT * FROM Params WHERE Name = 'DynamicallyUpdated';

-- Если строка найдена, аккуратно удалим её
DELETE FROM Params WHERE Name = 'DynamicallyUpdated';

После удаления этой записи и перезапуска службы сервера 1С платформа перестанет принудительно сбрасывать кэш при каждом действии пользователя.

Шаг 5. Настройка лимитов рабочих процессов кластера 1С

Чтобы обезопасить систему от лавинообразного падения процессов из-за возможных будущих утечек памяти в неоптимальном коде, правильно настроим параметры нашего кластера серверов 1С:

Проанализировав и последовательно выполнив все описанные шаги, мы сможем гарантированно выявить истинную причину перегрузки процессора нашего SQL-сервера, устранить неоптимальный код компиляции модулей, очистить переполненные таблицы обмена и вернуть производительность СУБД к нормальным штатным показателям.

← На главную