Почему падает сервер 1С при выполнении запроса и как оптимизировать расчет нарастающих итогов

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

Нередко разработчики сталкиваются с ситуацией, когда отлаженный годами запрос внезапно начинает вызывать падение службы 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. Если в таблице выборка 1 000 записей, результат соединения породит около 500 000 строк.
  2. Если записей 10 000, мы получим уже 50 000 000 строк.
  3. При 100 000 записей количество строк в промежуточном результате достигнет 5 миллиардов.

Даже если итоговая таблица после группировки будет компактной, SQL-серверу необходимо сначала построить этот гигантский промежуточный набор данных в tempdb и передать его серверу 1С для дальнейшей обработки. Именно здесь 32-битный сервер 1С сдается.

Ограничение адресного пространства 32-битного процесса

Выясним, почему падает именно 1С-сервер, а не MS SQL. Процесс rphost в 32-битной архитектуре имеет жесткое ограничение адресного пространства — 2 ГБ (в исключительных случаях 3 ГБ). Когда SQL-сервер начинает "выплескивать" результат огромного соединения в сторону сервера приложений, 1С пытается аллоцировать память под эти данные. Как только запрос требует больше памяти, чем может адресовать 32-битный процесс, происходит критический сбой Access Violation, и служба падает.

Почему же в файловой версии или на 64-битном сервере всё работает?

Способ 1: Перенос вычислений в код (Линейная сложность)

Самый правильный и надежный метод — отказаться от расчета нарастающего итога внутри запроса. Рассмотрим, как это сделать правильно. Мы должны получить из запроса простые данные, отсортированные по убыванию суммы, и обработать их в цикле.

Шаги реализации:

  1. Выполним запрос, который просто выбирает номенклатуру и сумму выручки.
  2. Установим сортировку в запросе УПОРЯДОЧИТЬ ПО СуммаВыручкиОборот УБЫВ.
  3. В коде 1С создадим переменную для накопления итога.

Выборка = Запрос.Выполнить().Выбрать();
НарастающийИтог = 0;
Пока Выборка.Следующий() Цикл
    НарастающийИтог = НарастающийИтог + Выборка.СуммаВыручкиОборот;
    // Теперь у нас есть все данные для определения категории ABC
    Категория = ОпределитьКатегорию(НарастающийИтог, ОбщаяСумма);
КонецЦикла;

Этот алгоритм имеет сложность O(N). Он отработает мгновенно даже на миллионе записей, не нагружая память сервера.

Способ 2: Использование СКД (Системы компоновки данных)

Если вам принципиально нужно получить готовый результат в отчете без написания кода обхода, используйте возможности СКД. Система компоновки данных умеет вычислять ресурсы с накоплением гораздо эффективнее, чем "чистый" SQL-запрос 1С.

Разберем использование функции ВычислитьВыражение. В поле выражения ресурса можно написать:

Сумма(СуммаВыручки) / ВычислитьВыражение("Сумма(СуммаВыручки)", , "ОбщийИтог") * 100

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

Способ 3: Оптимизация структуры запроса и данных

Проанализируем ситуацию, когда запрос "вчера работал, а сегодня падает". Часто это связано с качеством данных. Если в базе появилось много записей с нулевой выручкой или дублями в ключах аналитики, условие >= превращается практически в декартово произведение.

Что нужно проверить:

Технологический журнал как инструмент диагностики

Если причина падения остается неясной, рассмотрим настройку технологического журнала (ТЖ) для локализации ошибки. Нам интересны события 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-сервер сгенерировал из-за одного неоптимального условия в ПО, падение становится лишь вопросом времени и объема накопленных данных в базе.

← На главную