Многие разработчики и администраторы систем на базе 1С:Предприятие 8.3, использующие типовые конфигурации (такие как УТ 11, ERP или КА), сталкиваются с периодическими «зависаниями» при входе пользователей в систему. Одной из причин часто оказывается работа стандартной подсистемы «Текущие дела», которая обращается к регистру сведений ВерсииОбъектов. В этой статье мы подробно разберем, почему стандартный запрос может выполняться по 20 и более секунд, проанализируем планы выполнения на уровне СУБД и рассмотрим несколько стратегий оптимизации.
Функция ИнформацияОбУстаревшихВерсиях в библиотеке стандартных подсистем (БСП) предназначена для получения информации о количестве и объеме данных, которые можно очистить. Проблема заключается в структуре формируемого запроса. Рассмотрим типовой код, который генерирует нагрузку:
ВЫБРАТЬ
ЕСТЬNULL(СУММА(ВерсииОбъектов.РазмерДанных), 0) КАК РазмерДанных,
ЕСТЬNULL(СУММА(1), 0) КАК КоличествоВерсий
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
ВерсииОбъектов.ЕстьДанныеВерсии
И (ВерсииОбъектов.Объект ССЫЛКА Справочник.Номенклатура И ВерсииОбъектов.ДатаВерсии < &Граница1
ИЛИ ВерсииОбъектов.Объект ССЫЛКА Документ.ЗаказКлиента И ВерсииОбъектов.ДатаВерсии < &Граница2)
Когда типов объектов становится много, условие ГДЕ разрастается в огромную конструкцию с множеством ИЛИ. Для оптимизатора СУБД SQL Server такой запрос становится «тяжелым» из-за сложности эффективного использования индексов при наличии множественных условий по разным типам ссылок и диапазонам дат одновременно.
Проанализируем ситуацию, когда в консоли запросов всё работает быстро, а в технологическом журнале (ТЖ) мы видим длительные события DBMSSQL. Выясним причину, изучив план запроса (Execution Plan). Основная проблема часто кроется в операторе Clustered Index Seek, который на самом деле ведет себя как Scan.
Разберем, почему это происходит:
_Fld706 (Область данных). СУБД делает быстрый «поиск» (Seek) по этому полю, но дальше вынуждена сканировать миллионы строк, чтобы проверить соответствие полей _Fld13530_TYPE (Тип объекта) и _Fld13530_RTRef (Ссылка на таблицу объекта).СУММА), СУБД должна прочитать значения из поля РазмерДанных. Если этого поля нет в индексе, SQL Server обращается к основной таблице (Clustered Index), что при объеме регистра в несколько миллионов записей вызывает колоссальную нагрузку на дисковую подсистему и память.TempDB. Это мгновенно увеличивает время выполнения запроса с долей секунды до десятков секунд.Один из классических приемов оптимизации сложных запросов с ИЛИ — это разделение их на несколько подзапросов, объединенных через ОБЪЕДИНИТЬ ВСЕ. Это позволяет СУБД построить отдельный эффективный план для каждого типа объекта. Рассмотрим пример преобразованного запроса:
ВЫБРАТЬ
СУММА(Версии.РазмерДанных) КАК РазмерДанных,
СУММА(Версии.КоличествоВерсий) КАК КоличествоВерсий
ИЗ (
ВЫБРАТЬ
СУММА(ВерсииОбъектов.РазмерДанных) КАК РазмерДанных,
1 КАК КоличествоВерсий
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
ВерсииОбъектов.ЕстьДанныеВерсии
И ВерсииОбъектов.Объект ССЫЛКА Справочник.Номенклатура
И ВерсииОбъектов.ДатаВерсии < &Граница1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
СУММА(ВерсииОбъектов.РазмерДанных),
1
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
ВерсииОбъектов.ЕстьДанныеВерсии
И ВерсииОбъектов.Объект ССЫЛКА Документ.ЗаказКлиента
И ВерсииОбъектов.ДатаВерсии < &Граница1
) КАК Версии
Важный нюанс: Хотя этот метод делает планы запросов более предсказуемыми, он может не решить проблему кардинально, если в таблице все еще слишком много данных и отсутствуют подходящие индексы.
Если правка кода конфигурации затруднена, можно прибегнуть к административным мерам на уровне СУБД. Выясним, какого индекса не хватает системе. Часто СУБД сама подсказывает решение в Missing Index Details. Нам необходим индекс, который позволит СУБД получить все нужные данные (включая размер), не обращаясь к самой таблице.
Рассмотрим пример создания такого индекса (названия полей могут отличаться в зависимости от версии метаданных):
CREATE NONCLUSTERED INDEX [IX_ВерсииОбъектов_Optimization]
ON [dbo].[_InfoRg13529] ([_Fld18641], [_Fld706])
INCLUDE ([_Fld13530_TYPE], [_Fld13530_RTRef], [_Fld13534], [_Fld18639])
Где:
_Fld18641 — флаг ЕстьДанныеВерсии;_Fld706 — разделитель области данных;INCLUDE — включает поля объекта, даты и, самое главное, размера данных.Такой индекс называется покрывающим. С его помощью SQL Server выполнит Index Only Scan, что на порядок быстрее обращения к тяжелым страницам данных, где хранятся сами двоичные образы версий объектов.
Самый правильный способ решения проблемы в долгосрочной перспективе — это отказ от «тяжелых» запросов при открытии формы пользователем. Проанализируем, как это реализовано в последних версиях БСП:
Если вы используете старую версию УТ (например, 11.3), вы можете самостоятельно внедрить подобный механизм, создав небольшую обработку, которая будет считать итоги и записывать их в константу или отдельный регистр.
Помимо оптимизации кода и индексов, не забывайте о регламентных операциях в SQL Server:
ВерсииОбъектов постоянно добавляются и удаляются (при очистке), статистика быстро устаревает. Рекомендуется обновлять её ежедневно, желательно с FULLSCAN.TempDB.DBCC FREEPROCCACHE (с осторожностью!) или добавить хинт запроса для перекомпиляции плана, если это позволяет платформа.Подведем итог: оптимизация работы с версиями объектов требует комплексного подхода — от упрощения логики запроса в 1С до настройки специфических индексов на стороне SQL Server. Наилучший результат дает сочетание покрытия индексами и переход на фоновый расчет данных для пользовательского интерфейса.