При работе с высоконагруженными системами на базе 1С, такими как 1С:Документооборот, разработчики часто сталкиваются с проблемой «зависания» интерфейса при работе с динамическими списками. Особенно остро это проявляется при активации строк в списках задач или документов. В данной статье мы подробно разберем, что означает показатель Reads в SQL Profiler, почему он критически важен для производительности и как оптимизировать тяжелые запросы динамических списков, обремененные механизмами RLS (разграничение прав доступа на уровне записей).
Для начала проанализируем техническую сторону вопроса. Показатель Reads (чтения) в трассировке SQL Profiler или Extended Events отображает количество логических чтений страниц данных из буферного кэша SQL Server. Каждая страница имеет фиксированный размер — 8 КБ.
Рассмотрим, на что влияет высокий показатель Reads:
Reads при малом количестве результирующих строк почти всегда указывает на Index Scan (полное сканирование индекса) или Table Scan вместо эффективного Index Seek (поиска по индексу).Если в вашем профиле значение Reads исчисляется сотнями тысяч или миллионами, это означает, что для отображения всего 45 строк в динамическом списке сервер «перелопатил» гигабайты данных. Именно это является основной причиной высокого Duration (длительности выполнения).
Проанализируем типовой запрос задачи исполнителя, который часто становится источником проблем. В нем используются соединения с регистрами сведений для получения кешированной информации:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ЗадачаИсполнителя.Ссылка,
ЗадачаИсполнителя.Дата,
ЗадачаИсполнителя.Наименование,
ЗадачаИсполнителя.Выполнена,
ВЫБОР
КОГДА ЗадачаИсполнителя.Важность = ЗНАЧЕНИЕ(Перечисление.ВариантыВажностиЗадачи.Низкая) ТОГДА 0
КОГДА ЗадачаИсполнителя.Важность = ЗНАЧЕНИЕ(Перечисление.ВариантыВажностиЗадачи.Высокая) ТОГДА 2
ИНАЧЕ 1
КОНЕЦ КАК ВажностьКартинка,
ЗадачаИсполнителя.ПредметСтрокой,
КешИнформацииОбОбъектах.СостояниеКонтроля,
СведенияОПользователях.Подразделение КАК мгПодразделение
ИЗ
Задача.ЗадачаИсполнителя КАК ЗадачаИсполнителя
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РезультатыВыполненияПроцессовИЗадач КАК РезультатыВыполненияПроцессовИЗадач
ПО ЗадачаИсполнителя.Ссылка = РезультатыВыполненияПроцессовИЗадач.Объект
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КешИнформацииОбОбъектах КАК КешИнформацииОбОбъектах
ПО ЗадачаИсполнителя.Ссылка = КешИнформацииОбОбъектах.Объект
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
ПО ЗадачаИсполнителя.Автор = СведенияОПользователях.Пользователь
ГДЕ
ЗадачаИсполнителя.СостояниеБизнесПроцесса = ЗНАЧЕНИЕ(Перечисление.СостоянияБизнесПроцессов.Активен)
И НЕ ЗадачаИсполнителя.ПометкаУдаления
На первый взгляд запрос выглядит стандартно, однако ключевая проблема кроется в слове РАЗРЕШЕННЫЕ и поле ЗадачаИсполнителя.ПредметСтрокой.
Выясним причину, по которой запрос может выполняться 30 секунд. В 1С:Документооборот используется сложная система прав. Когда платформа встречает ВЫБРАТЬ РАЗРЕШЕННЫЕ, она трансформирует запрос в SQL, добавляя в секцию WHERE или в JOIN огромные подзапросы, проверяющие права текущего пользователя.
Рассмотрим, как это работает в связке с составными типами:
ПредметСтрокой в задачах часто связано с составным типом данных (Документы, Файлы, Письма, Мероприятия).LEFT JOIN к таблицам этих документов.В результате SQL Server видит настолько сложный запрос, что не может построить эффективный план. Он выбирает сканирование основной таблицы задач, надеясь по ходу дела найти первые 45 разрешенных записей. Если пользователь имеет доступ только к малой части задач, сервер просканирует всю таблицу до конца, что и даст нам огромный показатель Reads.
Чтобы понять, какие таблицы и индексы задействованы в сканировании, нам необходимо сопоставить имена таблиц в SQL (например, _InfoRg1896) с объектами 1С. Разберем, как это сделать программно. Воспользуемся методом ПолучитьСтруктуруХраненияБазыДанных().
Рассмотрим пример кода для получения структуры:
Структура = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого Таблица Из Структура Цикл
Если Таблица.ИмяТаблицыХранения = "_Task125" Тогда // Замените на имя из профайлера
Сообщить("Это таблица объекта: " + Таблица.Метаданные);
КонецЕсли;
КонецЦикла;
Проанализировав структуру, вы сможете понять, по какому именно индексу (например, ByField1896_TR) идет сканирование, и какие поля 1С в него входят. Это позволит вам добавить недостающие поля в индекс или пересмотреть условия в ГДЕ.
Рассмотрим основные способы ускорения динамического списка в подобных условиях:
ПредметСтрокой непосредственно в основном запросе списка. Иногда эффективнее вынести получение дополнительной информации в отдельное неявное соединение или использовать вычисляемые поля на стороне клиента, если это допустимо.РегистрСведений.КешИнформацииОбОбъектах. Убедитесь, что регламентные задания по обновлению этого кеша работают исправно. Если данные в кеше устарели или статистика SQL по этому регистру не актуальна, запрос будет тормозить.СостояниеБизнесПроцесса, Исполнитель, ПометкаУдаления), должны быть проиндексированы. Причем индекс должен быть составным и соответствовать порядку полей в запросе.Таким образом, высокий показатель Reads — это лишь симптом того, что сервер выполняет избыточную работу. В контексте 1С:Документооборота основной путь решения лежит через оптимизацию системы прав доступа и переход на современные механизмы работы с RLS.