Нередко разработчики сталкиваются с ситуацией, когда отлаженный годами запрос внезапно начинает вызывать падение службы 1С-сервера на конкретной базе данных. Проблема сопровождается пугающим сообщением: "На сервере 1С:Предприятие произошла неисправимая ошибка. Приложение будет закрыто". Особенно часто это проявляется на 32-битных серверах 1С, даже если сама СУБД (например, MS SQL Server) является 64-битной и обладает внушительным запасом оперативной памяти.
В этой статье мы подробно разберем, почему возникают такие падения, проанализируем механизмы работы СУБД с тяжелыми соединениями и рассмотрим эффективные способы решения проблемы без радикальной смены архитектуры предприятия.
Рассмотрим ситуацию на примере классического запроса для ABC-анализа. Проблема локализуется в блоке формирования таблицы тСуммаОборотовСуммаВсего, где используется соединение таблицы самой с собой по условию неравенства. Проанализируем этот фрагмент кода:
ВЫБРАТЬ
выборка.Номенклатура КАК Номенклатура,
МАКСИМУМ(ЕСТЬNULL(выборка.СуммаВыручкиОборот, 0)) КАК СуммаВыручкиОборот,
выборка.Характеристика КАК Характеристика,
СУММА(ЕСТЬNULL(выборка1.СуммаВыручкиОборот, 0)) КАК СуммаВыручкиОборотДляABC,
ЕСТЬNULL(тСуммаВсего.СуммаВсего, 0) КАК СуммаВсего
ПОМЕСТИТЬ тСуммаОборотовСуммаВсего
ИЗ
выборка КАК выборка
ЛЕВОЕ СОЕДИНЕНИЕ выборка КАК выборка1
ПО выборка.СуммаВыручкиОборот >= выборка1.СуммаВыручкиОборот
ЛЕВОЕ СОЕДИНЕНИЕ тСуммаВсего КАК тСуммаВсего
ПО (ИСТИНА)
СГРУППИРОВАТЬ ПО
выборка.Номенклатура,
выборка.Характеристика,
ЕСТЬNULL(тСуммаВсего.СуммаВсего, 0)
Основная "бомба" заложена в условии ПО выборка.СуммаВыручкиОборот >= выборка1.СуммаВыручкиОборот. Это так называемое "треугольное соединение" (Triangular Join). Его вычислительная сложность составляет O(N²). Давайте посчитаем, что происходит "под капотом" SQL-сервера:
выборка 1 000 записей, результат соединения породит около 500 000 строк.Даже если итоговая таблица после группировки будет компактной, SQL-серверу необходимо сначала построить этот гигантский промежуточный набор данных в tempdb и передать его серверу 1С для дальнейшей обработки. Именно здесь 32-битный сервер 1С сдается.
Выясним, почему падает именно 1С-сервер, а не MS SQL. Процесс rphost в 32-битной архитектуре имеет жесткое ограничение адресного пространства — 2 ГБ (в исключительных случаях 3 ГБ). Когда SQL-сервер начинает "выплескивать" результат огромного соединения в сторону сервера приложений, 1С пытается аллоцировать память под эти данные. Как только запрос требует больше памяти, чем может адресовать 32-битный процесс, происходит критический сбой Access Violation, и служба падает.
Почему же в файловой версии или на 64-битном сервере всё работает?
Самый правильный и надежный метод — отказаться от расчета нарастающего итога внутри запроса. Рассмотрим, как это сделать правильно. Мы должны получить из запроса простые данные, отсортированные по убыванию суммы, и обработать их в цикле.
Шаги реализации:
УПОРЯДОЧИТЬ ПО СуммаВыручкиОборот УБЫВ.
Выборка = Запрос.Выполнить().Выбрать();
НарастающийИтог = 0;
Пока Выборка.Следующий() Цикл
НарастающийИтог = НарастающийИтог + Выборка.СуммаВыручкиОборот;
// Теперь у нас есть все данные для определения категории ABC
Категория = ОпределитьКатегорию(НарастающийИтог, ОбщаяСумма);
КонецЦикла;
Этот алгоритм имеет сложность O(N). Он отработает мгновенно даже на миллионе записей, не нагружая память сервера.
Если вам принципиально нужно получить готовый результат в отчете без написания кода обхода, используйте возможности СКД. Система компоновки данных умеет вычислять ресурсы с накоплением гораздо эффективнее, чем "чистый" SQL-запрос 1С.
Разберем использование функции ВычислитьВыражение. В поле выражения ресурса можно написать:
Сумма(СуммаВыручки) / ВычислитьВыражение("Сумма(СуммаВыручки)", , "ОбщийИтог") * 100
СКД сначала получает плоскую таблицу данных, а затем за один проход вычисляет проценты и нарастающие итоги, что полностью исключает квадратичную нагрузку на сервер.
Проанализируем ситуацию, когда запрос "вчера работал, а сегодня падает". Часто это связано с качеством данных. Если в базе появилось много записей с нулевой выручкой или дублями в ключах аналитики, условие >= превращается практически в декартово произведение.
Что нужно проверить:
выборка не содержит дублирующихся строк по паре Номенклатура-Характеристика. Используйте СГРУППИРОВАТЬ ПО перед помещением во временную таблицу.ГДЕ СуммаВыручкиОборот > 0.Если причина падения остается неясной, рассмотрим настройку технологического журнала (ТЖ) для локализации ошибки. Нам интересны события EXCP (исключения) и MEM (мониторинг памяти).
Создайте файл logcfg.xml в каталоге conf сервера 1С со следующим содержанием:
<config xmlns="http://v8.1c.ru/7.4/config">
<log location="C:\LOGS\1C" history="24">
<event>
<eq property="Name" value="EXCP"/>
</event>
<event>
<eq property="Name" value="MEM"/>
</event>
</log>
</config>
После падения в логах вы, скорее всего, увидите ошибку "Memory allocation error" или "Out of memory". Это будет прямым доказательством того, что запрос превышает лимиты 32-битной системы.
Подведем итог нашему анализу. Расчет нарастающих итогов через соединения в запросе 1С — это опасная практика, которая "выстреливает" при росте объема данных.
Для стабильной работы системы рекомендуем следующее:
>=, <=, >, <) для больших таблиц.АвтономерЗаписи() в запросах, которая в некоторых случаях может помочь заменить сложные конструкции нумерации.Помните, что сервер 1С — это лишь посредник. Если вы заставляете его перекачивать через себя гигабайты промежуточных данных, которые SQL-сервер сгенерировал из-за одного неоптимального условия в ПО, падение становится лишь вопросом времени и объема накопленных данных в базе.