Почему медленно работает отбор в списке документов при включенном RLS и как это исправить

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке и сопровождении крупных информационных систем на базе 1С:Предприятие, таких как «БИТ.Строительство», программисты часто сталкиваются с деградацией производительности динамических списков. Ситуация выглядит следующим образом: под полными правами список открывается и фильтруется мгновенно, но как только за дело берется рядовой пользователь с ограниченными правами (RLS), время ожидания отбора по одному полю может вырастать с 2–3 до 25 секунд. Особенно критично это проявляется, когда в динамическом списке используется ЛЕВОЕ СОЕДИНЕНИЕ с регистрами сведений для получения дополнительных данных, например, статусов объектов.

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

Анализ исходного запроса динамического списка

Рассмотрим типовой запрос динамического списка, который часто используется для отображения документов с их текущими статусами. В нашем примере это документ бит_ЗаявкаНаРасходованиеСредств и регистр сведений бит_СтатусыОбъектов.


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    Документ.Ссылка КАК Ссылка,
    Документ.Номер КАК Номер,
    Документ.Дата КАК Дата,
    Документ.Организация КАК Организация,
    Документ.Сумма КАК Сумма,
    // ... другие поля документа ...
    ЕСТЬNULL(СтатусыОбъектов.Статус, ЗНАЧЕНИЕ(Справочник.бит_СтатусыОбъектов.ПустаяСсылка)) КАК Статус,
    ЕСТЬNULL(СтатусыОбъектов.ДатаИзмененияСтатуса, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаИзмененияСтатуса
ИЗ
    Документ.бит_ЗаявкаНаРасходованиеСредств КАК Документ
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.бит_СтатусыОбъектов КАК СтатусыОбъектов
        ПО Документ.Ссылка = СтатусыОбъектов.Объект
            И (СтатусыОбъектов.ВидСтатуса = ЗНАЧЕНИЕ(Перечисление.бит_ВидыСтатусовОбъектов.Статус))

Проанализируем ситуацию: пользователь ставит отбор по полю Статус. Если статус «Черновик» (которых в базе мало), список выводится за 3 секунды. Если статус «Утвержден» (которых большинство), время ожидания увеличивается до 10–20 секунд. Выясним причину такого поведения.

Влияние RLS на производительность соединений

Когда в запросе присутствует ключевое слово РАЗРЕШЕННЫЕ и у пользователя настроено ограничение доступа (например, через шаблон #ПоНаборамЗначений), платформа 1С на уровне SQL-запроса добавляет сложные условия фильтрации. Эти условия обычно представляют собой вложенные запросы к таблицам прав доступа.

Проблема заключается в том, что SQL-серверу необходимо выполнить LEFT JOIN между основной таблицей документов, таблицей регистра статусов и динамически созданной таблицей разрешенных ссылок. Если отбор устанавливается по полю присоединенной таблицы (в нашем случае Статус), оптимизатор SQL может выбрать неверный план выполнения. Для небольшого количества «Черновиков» он использует индексный поиск, а для большого количества «Утвержденных» документов может перейти к сканированию всей таблицы (Scan), что при наличии RLS превращается в тяжелую вычислительную задачу.

Проблема составных типов данных в условиях соединения

Одной из скрытых причин медленной работы является использование полей с составным типом данных в условиях соединения или фильтрации. В регистре бит_СтатусыОбъектов поле ВидСтатуса часто имеет составной тип. Посмотрим на условие:

И (СтатусыОбъектов.ВидСтатуса = ЗНАЧЕНИЕ(Перечисление.бит_ВидыСтатусовОбъектов.Статус))

При работе с составными типами SQL-сервер не всегда может эффективно использовать индексы. Чтобы помочь системе, необходимо явно ограничить тип данных. Разберем, как это сделать с помощью функции ВЫРАЗИТЬ.

Использование функции ВЫРАЗИТЬ для оптимизации

Если поле ВидСтатуса является составным, рекомендуется переписать условие соединения, явно указав ожидаемый тип. Это позволит SQL-серверу использовать индекс по конкретному полю таблицы, а не выполнять сложные преобразования типов «на лету».

Пример оптимизированного фрагмента запроса:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.бит_СтатусыОбъектов КАК СтатусыОбъектов
ПО Документ.Ссылка = СтатусыОбъектов.Объект
    И (ВЫРАЗИТЬ(СтатусыОбъектов.ВидСтатуса КАК Перечисление.бит_ВидыСтатусовОбъектов) = ЗНАЧЕНИЕ(Перечисление.бит_ВидыСтатусовОбъектов.Статус))

Важный момент: убедитесь, что в регистре сведений бит_СтатусыОбъектов измерения Объект и ВидСтатуса проиндексированы. Если регистр непериодический, индекс по измерениям создается автоматически, но порядок измерений в индексе имеет значение для скорости поиска.

Параметрическое «обнюхивание» (Parameter Sniffing)

Разная скорость работы при разных значениях отбора («Черновик» vs «Утвержден») часто обусловлена механизмом Parameter Sniffing на стороне MS SQL Server. Когда строится план запроса, SQL-сервер ориентируется на первое переданное значение параметра. Если первым был «Черновик», план будет оптимизирован под выборку малого количества данных. При последующем запросе с параметром «Утвержден» этот план может оказаться крайне неэффективным.

Для решения этой проблемы на уровне СУБД можно обновить статистики, но в контексте 1С более надежным способом будет упрощение самого запроса динамического списка, чтобы уменьшить вариативность планов исполнения.

Рекомендации по кардинальному ускорению списка

Если манипуляции с ВЫРАЗИТЬ и индексами не привели к желаемому результату (вывод списка все еще занимает более 5 секунд), проанализируем архитектурные решения:

  1. Денизоляция данных. Самый эффективный способ ускорить динамический список с RLS — избавиться от ЛЕВОЕ СОЕДИНЕНИЕ. Для этого текущий статус документа можно дублировать в реквизит самого документа бит_ЗаявкаНаРасходованиеСредств. При изменении статуса в регистре необходимо программно обновлять реквизит в документе. В этом случае RLS будет работать с одной физической таблицей, что в разы быстрее.
  2. Упрощение RLS. Проверьте, сколько ролей с RLS назначено пользователю. Если у пользователя 5 ролей на один документ и в каждой свой RLS, платформа 1С объединит их через OR (ИЛИ), что создаст огромную нагрузку на SQL. По возможности старайтесь сводить ограничения к одной лаконичной роли.
  3. Режим «Динамическое считывание данных». В свойствах динамического списка на форме убедитесь, что флаг «Динамическое считывание данных» включен. Это позволит платформе запрашивать данные порциями, а не пытаться вычислить все разрешенные ссылки сразу.
  4. Индексация реквизитов. Если пользователь делает отбор по полю Статус, это поле (даже если оно в присоединенной таблице) должно быть проиндексировано. В случае с регистром сведений убедитесь, что ресурс или реквизит Статус имеет свойство Индексировать.

Подведем итог: долгая работа отборов в списках 1С при наличии RLS — это почти всегда результат неоптимального плана запроса SQL из-за сложных соединений. Использование ВЫРАЗИТЬ, контроль индексов и, в крайнем случае, отказ от соединений в пользу реквизитов документа позволяют добиться мгновенного отклика интерфейса даже при жестких ограничениях прав доступа.

← На главную