При администрировании крупных информационных баз на платформе «1С:Предприятие» разработчики и системные администраторы часто сталкиваются с анализом производительности СУБД. В процессе исследования технологического журнала (ТЖ) или трассировок SQL Server можно обнаружить весьма специфические запросы, содержащие жестко заданное ограничение SELECT TOP 25000. На первый взгляд, такая конструкция кажется странной, особенно если в самом коде конфигурации подобные лимиты явно не прописывались.
В рамках данной статьи мы подробно разберем, в каких именно случаях платформа 1С генерирует подобные запросы, по какому алгоритму они строятся, почему выбран именно этот лимит и как минимизировать негативное влияние таких операций на общую производительность системы.
Проанализируем представленный в теме SQL-запрос. Мы видим, что платформа выполняет выборку данных из таблицы документа _Document391 (в терминах 1С это может быть, например, документ «Поступление на расчетный счет») и его табличной части _Document391_VT10469 («Расшифровка платежа»). Результат запроса помещается во временную таблицу СУБД:
INSERT INTO #tt69 WITH(TABLOCK) (_C_1RRef, _C_2_TYPE, ...)
SELECT TOP 25000 T1._IDRRef, ...
FROM dbo._Document391 T1
...
В условиях выборки WHERE присутствует массовый поиск по полям субконто и реквизитам табличных частей. Платформа ищет ссылки определенного типа (в данном случае тип идентифицируется шестнадцатеричным кодом 0x00000192) и конкретных значений, передаваемых через параметры СУБД (@P2, @P3 и так далее).
Такой сложный и «тяжелый» запрос с использованием множественных логических операторов OR EXISTS и условий по разным полям шапки и табличной части характерна для поиска связей между объектами. Это подводит нас к главной причине появления этой конструкции.
Конструкция SELECT TOP 25000 является жестко зашитым на уровне платформы «1С:Предприятие» ограничением (встроенным в слой трансляции SDBL — System Database Layer). Она генерируется исключительно платформенными механизмами контроля ссылочной целостности в следующих случаях:
НайтиПоСсылкам(): Когда разработчик программно вызывает этот метод для поиска всех мест использования определенных элементов справочников или документов.Удалить(): При попытке удаления объекта объектная техника платформы неявно выполняет проверку на наличие существующих ссылок в базе данных, чтобы не допустить появления «битых» ссылок.Таким образом, когда система пытается удалить объект или найти его использование, она выполняет каскадный обход метаданных. Для каждой таблицы, которая теоретически может содержать ссылку на проверяемый объект, платформа формирует индивидуальный SQL-запрос с ограничением TOP 25000.
Выбор именно этого числового значения обусловлен архитектурными компромиссами разработчиков платформы 1С:
rphost.#tt69) порциями. При превышении лимита в 25 000 записей алгоритм может остановить дальнейшее сканирование конкретной таблицы и перейти к следующей, экономя время выполнения операции.Несмотря на заложенную оптимизацию, запросы с SELECT TOP 25000 могут стать источником серьезных проблем с производительностью и вызывать массовые транзакционные блокировки в СУБД. Выясним, почему это происходит.
Главная опасность кроется в использовании реквизитов с **составным типом данных**, особенно если в состав входит неограниченный тип ЛюбаяСсылка (в терминах СУБД — AnyRef). Когда система ищет ссылки на объект, тип которого может быть записан в такой составной реквизит, платформа вынуждена генерировать аналогичные запросы ко всем физическим таблицам СУБД, где этот реквизит объявлен.
В результате СУБД выполняет сканирование таблиц (Table Scan или Index Scan) вместо быстрого поиска по индексу (Index Seek), поскольку СУБД не может эффективно использовать индексы при сложных условиях с OR EXISTS по нескольким полям и табличным частям. Это приводит к лавинообразному росту нагрузки на дисковую подсистему и процессор сервера.
Кроме того, использование хинта WITH(TABLOCK) при записи во временные таблицы СУБД при массовом параллельном поиске ссылок может приводить к монопольным блокировкам системной базы данных tempdb, парализуя работу остальных пользователей.
Если в вашей базе данных начали регулярно фиксироваться длительные запросы с конструкцией SELECT TOP 25000, разберем по шагам план действий для исправления ситуации:
DBMSSQL с отбором по подстроке "SELECT TOP 25000". Анализируя свойство Context, вы легко определите конкретный модуль 1С и строчку кода, которая инициировала вызов (например, конкретную кнопку на форме или регламентное задание).ПолучитьСтруктуруХраненияБазыДанных(), чтобы сопоставить имена таблиц СУБД (например, _Document391) с реальными объектами конфигурации.ЛюбаяСсылка и избыточно широких составных типов в реквизитах документов и справочников. Сужение типа данных существенно сократит количество таблиц, опрашиваемых платформой при поиске ссылок.НайтиПоСсылкам() для всей базы данных. Если бизнес-логика позволяет, лучше написать точечный и быстрый запрос к конкретным таблицам, где эти связи наиболее вероятны.Рассмотрим пример того, как можно оптимизировать программную проверку наличия ссылок, избегая тяжелого платформенного механизма:
// Вместо медленного НайтиПоСсылкам([МассивСсылок])
// Используем точечный запрос к известным местам использования
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг
|ГДЕ
| Контрагент = &СсылкаДляПроверки";
Запрос.УстановитьПараметр("СсылкаДляПроверки", ПроверяемыйКонтрагент);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
ВызватьИсключение "Объект нельзя удалить, так как он используется в продажах!";
КонецЕсли;
Использование подобных точечных проверок в критически важных участках кода позволяет полностью обойти генерацию платформенных запросов SELECT TOP 25000, снижая нагрузку на СУБД и предотвращая избыточное наложение транзакционных блокировок.