Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда тяжелый отчет, построенный на компоновке данных (СКД), внезапно прерывает свою работу. При этом в Журнале регистрации нет никаких внятных ошибок — он просто обрывается на полуслове, а пользователи жалуются на сообщение: «Аварийно завершился рабочий процесс фонового задания». Примечательно, что если ограничить выборку (например, использовать конструкцию ПЕРВЫЕ 200), то отчет прекрасно формируется.
В этой статье мы подробно разберем, почему один отчет может работать 12 часов и успешно завершаться, а другой «роняет» рабочий процесс rphost всего за 5 минут. Мы детально изучим инструменты диагностики этой проблемы, проанализируем настройки сервера 1С и СУБД, а также научимся правильно оптимизировать код запросов, чтобы избежать подобных сбоев в будущем.
Для понимания природы этой проблемы давайте проанализируем динамику потребления оперативной памяти сервером приложений 1С:
rphost не испытывает резких скачков по памяти, так как объем данных, находящийся в оперативной памяти единовременно, остается в пределах нормы.rphost лавинообразно съедает всю выделенную ему оперативную память на сервере. В этот момент операционная система или менеджер кластера 1С (rmngr) принудительно «убивают» этот процесс для предотвращения зависания всей операционной системы. Именно поэтому в Журнал регистрации 1С не успевает записаться ошибка — процесс уничтожается мгновенно.Основным триггером такого поведения практически всегда является декартово произведение (Cartesian Product) в запросе либо некорректная работа с временными таблицами. При ограничении выборки с помощью ПЕРВЫЕ 200 объем промежуточных данных физически мал, и сервер успевает справиться с нагрузкой. Но на реальном объеме данных неоптимальный запрос генерирует в оперативной памяти миллионы или даже миллиарды строк, приводя к моментальному коллапсу.
Поскольку Журнал регистрации не дает полезной информации, нам придется задействовать более глубокие инструменты анализа. Разберем по шагам, как локализовать ошибку.
Это самый надежный способ узнать, какой именно запрос или кусок кода привел к падению. Для этого на сервере 1С в каталоге conf (обычно это путь C:\Program Files\1cv8\conf для Windows) необходимо создать конфигурационный файл logcfg.xml.
Проанализируем пример конфигурации logcfg.xml, которая собирает только критические события и контекст их возникновения, не перегружая дисковую подсистему сервера:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\v8\logs" history="12">
<event>
<eq property="Name" value="EXCP"/>
</event>
<event>
<eq property="Name" value="EXCPCNTX"/>
</event>
<event>
<eq property="Name" value="PROC"/>
</event>
<property name="all"/>
</log>
</config>
После сохранения файла и воспроизведения падения отчета перейдем в каталог C:\v8\logs. Нам понадобятся папки с именами упавших процессов rphost_<PID>. Откроем последний лог-файл перед моментом аварии. В самом конце файла ищем строку с событием EXCP или EXCPCNTX. В параметре Context будет четко указана строка модуля или текст запроса, на котором процесс прекратил свое существование.
Если Технологический журнал пуст, значит, операционная система завершила процесс жестко, не дав платформе 1С обработать исключение.
Application Error и кодом события (Event ID) 1000 или 1001 для исполняемого файла rphost.exe. В описании ошибки обратим внимание на «сбойный модуль» — это может быть библиотека платформы 1С или СУБД.dmesg -T | grep -i -E 'oom|kill' или изучив файл /var/log/messages. Если процесс завершил механизм защиты от переполнения памяти OOM Killer (Out-Of-Memory Killer), мы увидим запись вида Killed process rphost. Это прямое подтверждение нехватки физической ОЗУ.Платформа 1С при падении процессов пытается сгенерировать мини-дампы памяти. По умолчанию они складываются в папку %LOCALAPPDATA%\1C\1cv8\dumps (или в каталог профиля пользователя, под которым запущена служба сервера 1С). Наличие файлов с расширением .dmp подтверждает критический сбой памяти (например, ошибку нарушения прав доступа к памяти Access Violation).
Часто рабочий процесс гасится не операционной системой, а самим менеджером кластера 1С rmngr в соответствии с заданными административными настройками.
Откроем консоль администрирования кластера серверов 1С, перейдем в свойства нашего рабочего сервера и проверим следующие параметры:
Если эти параметры настроены некорректно или не настроены вовсе, то при достижении предела в 95% от общего объема памяти сервера процесс rphost будет принудительно перезапущен. Если ваш сервер 32-разрядный (даже при работе на 64-битной Windows), помните, что один процесс rphost физически не может адресовать более 2-3 Гб памяти. В этом случае единственным решением будет переход на 64-разрядный сервер 1С.
Теперь перейдем к исправлению самой уязвимости в коде отчета. Рассмотрим основные практические методы оптимизации запросов для СКД.
Никогда не соединяйте неиндексированные временные таблицы с физическими таблицами (особенно с крупными регистрами сведений или накопления). СУБД вынуждена выполнять полное сканирование (Table Scan) и строить огромные хэш-таблицы в оперативной памяти.
Разберем пример создания временной таблицы. Предположим, у нас есть неоптимальный запрос:
ВЫБРАТЬ
Реализация.Ссылка КАК ДокументСсылка,
Реализация.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Документы
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Документы.ДокументСсылка,
Цены.Цена
ИЗ
ВТ_Документы КАК ВТ_Документы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО ВТ_Документы.ДокументСсылка = Цены.Документ
В данном случае, если документов в таблице много, соединение с регистром без индекса вызовет колоссальную нагрузку. Исправим это, добавив индексирование:
ВЫБРАТЬ
Реализация.Ссылка КАК ДокументСсылка,
Реализация.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Документы
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ИНДЕКСИРОВАТЬ ПО
ДокументСсылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Документы.ДокументСсылка,
Цены.Цена
ИЗ
ВТ_Документы КАК ВТ_Документы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО ВТ_Документы.ДокументСсылка = Цены.Документ
Внимательно проанализируем условия связи в запросе. Если мы связываем таблицы по неполному набору ключевых полей, строки начинают множиться геометрически. Например, связывая таблицы по полю Номенклатура, но забывая про поле Характеристика или Склад, мы рискуем получить миллиарды комбинаций строк, которые моментально «свалят» процесс по памяти.
Механизм автозаполнения в СКД — очень удобная штука, но на сложных запросах он работает непредсказуемо. Система пытается автоматически добавить связи, условия и виртуальные таблицы СУБД, что может превратить легкий запрос в монстра со множеством избыточных соединений.
Рекомендуется отключить флаг «Автозаполнение» на вкладке «Наборы данных» и настроить все доступные поля, параметры и связи наборов данных вручную.
Иногда даже идеальный запрос выполняется неоптимально из-за того, что СУБД не знает реального распределения данных в таблицах. Оптимизатор СУБД может выбрать неверный план выполнения (например, Nested Loops вместо Hash Match), решив прокачать терабайты данных через оперативную память. Регулярно настраивайте регламентные процедуры СУБД, включающие в себя дефрагментацию индексов и обновление статистики.
Если ваш отчет СКД аварийно завершает работу:
EXCP.OOM Killer или ошибок приложения.