При активной работе пользователей и выполнении тяжелых отчетов или регламентных заданий системная база tempdb в MS SQL Server может лавинообразно увеличиваться в размерах. Разберем, влияют ли на этот процесс вложенные запросы, почему прямое отслеживание таблиц вида #tt не дает желаемого результата и как правильно диагностировать проблемы производительности.
Рассмотрим распространенное заблуждение: многие разработчики считают, что только физические временные таблицы расходуют дисковое пространство tempdb, а вложенные запросы работают исключительно в оперативной памяти. На практике ситуация выглядит иначе:
ПОМЕСТИТЬ платформа 1С создает в tempdb таблицы вида #tt<N>. Если объем данных невелик, они могут кэшироваться в буферном пуле, но при больших выборках занимают реальное дисковое пространство.Read Committed Snapshot Isolation (RCSI). Все предыдущие версии изменяемых строк сохраняются в tempdb. Длительные транзакции блокируют очистку устаревших версий, что является наиболее частой причиной неконтролируемого роста tempdb.Проанализируем структуру именования временных таблиц в MS SQL Server. Когда в коде 1С мы помещаем данные во временную таблицу, SQL Server добавляет к ее имени уникальный суффикс из символов подчеркивания и шестнадцатеричного идентификатора (например, #tt1___________________000000000000001A). Это сделано для изоляции пространств имен разных пользовательских сессий.
Пытаться оценить размер конкретной временной таблицы через системные представления только по ее имени неэффективно: в системе одновременно могут существовать десятки таблиц с префиксом #tt1 от разных сеансов. Вместо ручного перебора имен таблиц необходимо исследовать план выполнения запроса и потребление ресурсов конкретными сессиями.
Когда на сервере работает множество пользователей под технической учетной записью (например, sa или usr1cv8), отфильтровать нужный запрос стандартными средствами бывает непросто. Рассмотрим эффективные способы локализации запроса.
Способ 1. Использование уникального маркера в тексте запроса
Добавим в текст исследуемого запроса фиктивное поле с уникальной строкой. Это позволит мгновенно найти нужный SQL-батч в SQL Server Profiler или сессии Extended Events по фильтру текста:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ""МАРКЕР_ОТЛАДКИ_100500"" КАК ИдентификаторОтладки,
| ТоварыНаСкладах.Номенклатура КАК Номенклатура,
| ТоварыНаСкладах.КоличествоОстаток КАК Количество
|ПОМЕСТИТЬ ВТ_Остатки
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладах
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Остатки.Номенклатура,
| ВТ_Остатки.Количество
|ИЗ
| ВТ_Остатки КАК ВТ_Остатки";
Способ 2. Локализация через Технологический журнал (ТЖ) 1С
Настроим сбор событий технологического журнала для выявления контекста исполнения запроса. С помощью фильтра по имени процесса p:processname (имя рабочей базы) и событию DBMSSQL мы получим точный текст SQL-запроса, длительность его выполнения и номер системного соединения (t:connectID):
<log location="C:\LOGS\TJ_TEMPDB" history="4">
<event>
<eq property="name" value="DBMSSQL"/>
<eq property="p:processname" value="ИмяНашейБазы"/>
<ge property="duration" value="100000"/>
</event>
<property name="all"/>
</log>
Выясним, какие именно сессии создают нагрузку на tempdb, выполнив диагностический запрос в среде SQL Server Management Studio. Для этого используем динамические административные представления sys.dm_db_session_space_usage и sys.dm_exec_sessions:
SELECT
s.session_id,
s.login_name,
s.host_name,
s.program_name,
(su.user_objects_alloc_page_count - su.user_objects_dealloc_page_count) * 8 / 1024 AS UserObjectsMB,
(su.internal_objects_alloc_page_count - su.internal_objects_dealloc_page_count) * 8 / 1024 AS InternalObjectsMB
FROM sys.dm_db_session_space_usage su
JOIN sys.dm_exec_sessions s ON su.session_id = s.session_id
WHERE (su.user_objects_alloc_page_count + su.internal_objects_alloc_page_count) > 0
ORDER BY UserObjectsMB + InternalObjectsMB DESC;
В выводе запроса обратим внимание на две колонки:
МенеджерВременныхТаблиц, не ждем окончания работы процедуры или закрытия соединения. Как только данные таблицы обработаны, вызываем оператор УНИЧТОЖИТЬ ВТ_ИмяТаблицы. Это освободит страницы в tempdb немедленно.ИНДЕКСИРОВАТЬ ПО для временных таблиц, содержащих более сотни записей, если они участвуют в дальнейших соединениях или фильтрах. Без индексов СУБД выполняет полное сканирование, порождая паразитные сбросы данных в служебные таблицы.Sort Warnings или Hash Warnings, свидетельствующие о нехватке оперативной памяти.