Эффективное администрирование баз данных немыслимо без возможности retrospektive (ретроспективного) анализа. Архив выполненных запросов — это не просто лог-файл‚ а стратегический актив для DBA и разработчиков‚ позволяющий выявлять узкие места‚ расследовать инциденты безопасности‚ оптимизировать производительность и аудитить изменения данных. В этой статье мы подробно разберем механизмы сбора‚ хранения и анализа истории запросов в экосистеме Microsoft SQL Server и смежных технологиях.
Уровни архивирования: от мгновенного снимка к долгосрочному хранилищу
В SQL Server существует три основных уровня доступа к истории запросов‚ различающихся по глубине детализации‚ накладным расходам и сроку хранения:
- Текущая активность (Real-time): Динамические административные представления (DMV) —
sys.dm_exec_requests‚sys.dm_exec_sessions‚sys.dm_exec_query_stats. Показывают‚ что происходит прямо сейчас или кэшированные планы недавних выполнений. - Хранилище запросов (Query Store): Встроенный механизм (начиная с SQL Server 2016 / Azure SQL)‚ автоматически захватывающий текст запросов‚ планы выполнения и статистику рантайма на диск. Это основной инструмент для долгосрочного анализа производительности.
- Событийная трассировка (Extended Events / Profiler): Низкоуровневый аудит каждого события (RPC:Completed‚ SQL:BatchCompleted). Дает максимальную детализацию‚ но требует ручной настройки и управления файлами трассировки.
Query Store: «Черный ящик» производительности
Query Store (Хранилище запросов), это рекомендуемый Microsoft способ ведения архива выполненных запросов. В отличие от кэша планов (sys.dm_exec_query_stats)‚ который очищается при перезапуске сервера или нехватке памяти‚ Query Store сохраняет данные на диск.
Архитектура и компоненты
Данные в Query Store разделены на логические хранилища (внутренние таблицы‚ доступные через каталоговые представления):
- Query Text Store (sys.query_store_query_text): Уникальные тексты запросов (параметризованные).
- Query Store (sys.query_store_query): Связь текста с контекстом (настройки SET-опций‚ схема). Один текст может иметь несколько записей здесь.
- Plan Store (sys.query_store_plan): Планы выполнения (XML)‚ привязанные к конкретному Query ID.
- Runtime Stats Store (sys.query_store_runtime_stats / sys.query_store_runtime_stats_interval): Агрегированная статистика выполнения (CPU‚ Duration‚ Logical Reads‚ Row Count и т.д.) за интервалы времени (по умолчанию 1 час).
Ключевой запрос для анализа архива
Ниже приведен базовый запрос‚ объединяющий все сущности для получения полной картины: текста запроса‚ плана‚ статистики компиляции и времени выполнения.
<!-- Пример кода для статьи -->
SELECT
Txt.query_text_id‚
Txt.query_sql_text‚
Pln.plan_id‚
Qry.query_id‚
RtSt.runtime_stats_id‚
RtSt.avg_duration‚
RtSt.avg_cpu_time‚
RtSt.avg_logical_io_reads‚
RtSt.count_executions‚
Rsi.start_time AS interval_start‚
Rsi.end_time AS interval_end
FROM sys.query_store_plan AS Pln
INNER JOIN sys.query_store_query AS Qry
ON Pln.query_id = Qry.query_id
INNER JOIN sys.query_store_query_text AS Txt
ON Qry.query_text_id = Txt.query_text_id
INNER JOIN sys.query_store_runtime_stats AS RtSt
ON Pln.plan_id = RtSt.plan_id
INNER JOIN sys.query_store_runtime_stats_interval AS Rsi
ON RtSt.runtime_stats_interval_id = Rsi.runtime_stats_interval_id
WHERE Rsi.start_time > DATEADD(DAY‚ -7‚ GETUTCDATE) -- Последние 7 дней
ORDER BY RtSt.avg_duration DESC;
Настройка сброса на диск (Data Flush Interval)
Важно понимать: статистика рантайма (Runtime Stats) сначала накапливается в памяти. Параметр DATA_FLUSH_INTERVAL_SECONDS (по умолчанию 900 сек / 15 мин) определяет‚ как часто данные гарантированно попадают на диск.
ALTER DATABASE [YourDB] SET QUERY_STORE (DATA_FLUSH_INTERVAL_SECONDS = 60);
Совет: Для систем с высокой требовательностью к сохранности данных (аудит‚ расследование инцидентов) снизьте этот интервал до 60 секунд‚ принимая незначительную нагрузку на подсистему ввода-вывода.
DMV для оперативного доступа: sys.dm_exec_requests и sys.dm_exec_query_stats
Если Query Store отключен или нужно посмотреть на «живую» нагрузку прямо сейчас‚ используются динамические представления.
Анализ текущих выполняющихся запросов
Полезен для поиска блокировок (blocking)‚ долгих операций и понимания текущей нагрузки.
SELECT
der.session_id‚
der.status‚
der.command‚
der.wait_type‚
der.wait_time‚
der.blocking_session_id AS blocker_spid‚
DB_NAME(der.database_id) AS database_name‚
s.login_name‚
s.host_name‚
s.program_name‚
LEFT((SELECT text FROM sys.dm_exec_sql_text(der.sql_handle))‚ 500) AS current_sql_text‚
der.start_time‚
der.total_elapsed_time / 1000.0 AS elapsed_time_sec‚
der.cpu_time / 1000.0 AS cpu_time_sec‚
der.logical_reads
FROM sys.dm_exec_requests der
LEFT JOIN sys.dm_exec_sessions s
ON s.session_id = der.session_id
WHERE der.session_id > 50 -- Исключаем системные процессы
AND der.session_id <> @@SPID -- Исключаем свой запрос
ORDER BY der.total_elapsed_time DESC;
История из кэша планов (sys.dm_exec_query_stats)
Хранит агрегаты по запросам‚ планы которых сейчас в кэше. Внимание: Данные теряются при перезапуске сервера‚ сбросе кэша (DBCC FREEPROCCACHE) или вытеснении по LRU.
SELECT TOP 50
qs.total_elapsed_time / qs.execution_count / 1000.0 AS avg_elapsed_ms‚
qs.total_worker_time / qs.execution_count / 1000.0 AS avg_cpu_ms‚
qs.total_logical_reads / qs.execution_count AS avg_logical_reads‚
qs.execution_count‚
qs.last_execution_time‚
qs.creation_time‚
SUBSTRING(st.text‚ (qs.statement_start_offset/2) + 1‚
((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text)
ELSE qs.statement_end_offset END ⎼ qs.statement_start_offset)/2) + 1) AS statement_text‚
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY qs.total_elapsed_time / qs.execution_count DESC;
Определение времени последнего обращения к объектам
Частая задача: «Когда последний раз запрашивалась эта таблица?». Прямого лога «SELECT * FROM Table» в стандартном SQL Server нет (если не настроен Audit/XE)‚ но можно использовать статистику индексов.
sys.dm_db_index_usage_stats
Это представление сбрасывается при перезапуске сервера‚ но показывает факты обращений к индексам (Seek‚ Scan‚ Lookup‚ Update).
SELECT
OBJECT_NAME(ius.object_id‚ ius.database_id) AS table_name‚
i.name AS index_name‚
ius.last_user_seek‚
ius.last_user_scan‚
ius.last_user_lookup‚
ius.last_user_update‚
ius.user_seeks‚
ius.user_scans‚
ius.user_lookups
FROM sys.dm_db_index_usage_stats ius
JOIN sys.indexes i
ON i.object_id = ius.object_id AND i.index_id = ius.index_id
WHERE ius.database_id = DB_ID
AND OBJECTPROPERTY(ius.object_id‚ 'IsUserTable') = 1
ORDER BY
GREATEST(ius.last_user_seek‚ ius.last_user_scan‚ ius.last_user_lookup) DESC;
Примечание: Функция GREATEST доступна в Azure SQL / SQL Server 2022+. В старых версиях используйте CASE/IIF.
Инструменты расширенного аудита и логирования
Когда встроенных DMV и Query Store недостаточно (нужен полный аудит безопасности‚ логирование всех ошибок‚ параметров вызовов)‚ применяются:
- Extended Events (XEvents): Наследники Profiler. Легковесные‚ мощные‚ позволяют писать в файл (event_file) или кольцевой буфер. Сессия
system_healthработает по умолчанию.- События:
sql_batch_completed‚rpc_completed‚error_reported. - Поля:
sql_text‚username‚client_hostname‚duration.
- События:
- SQL Server Audit: Настроен на уровне сервера/БД для соответствия стандартам (GDPR‚ HIPAA‚ PCI DSS). Пишет в файл безопасности Windows или файл аудита SQL.
- Transaction Log (fn_dblog / fn_dump_dblog): Позволяет увидеть кто и что менял (INSERT/UPDATE/DELETE)‚ но не показывает чистые SELECT; Требует знания LSN и понимания внутренней структуры.
Клиентские инструменты и IDE: локальная история
Не стоит забывать‚ что история запросов часто нужна разработчику локально.
- SSMS (SQL Server Management Studio): Не имеет встроенного постоянного архива истории вкладок. Решается плагинами: SSMS Tools Pack (функция «Window Connection Coloring» и «Query History»)‚ SQL History (Redgate).
- DataGrip / JetBrains IDEs: Имеют мощный SQL Log (
Help | Show SQL Log). Записывает абсолютно все запросы‚ посланные драйвером (включая служебные метаданные). Сохраняется в текстовые файлы с ротацией. - Azure Data Studio: Расширения «Query History» позволяют сохранять выполненные скрипты в локальный JSON/SQLite.
Облачные платформы: Fabric‚ Azure SQL‚ Databricks
Современные PaaS-решения выносят мониторинг на уровень выше экземпляра сервера.
Microsoft Fabric / Azure SQL: Query Activity
В Microsoft Fabric (и современных версиях Azure SQL) доступен интерфейс Query Activity (Активность запросов). Это единая панель‚ объединяющая:
- Выполняющиеся сейчас запросы (Real-time).
- Историю выполненных запросов (Completed).
- Топ по длительности / частоте / ресурсам.
Преимущество: Не нужно писать T-SQL код для DMV — UI позволяет фильтровать по времени‚ пользователю‚ базе‚ тексту запроса и сразу смотреть план выполнения.
Azure Databricks SQL: Query History
В Databricks SQL история запросов доступна в UI и через системные таблицы (system.query.history). Показывает: полный текст‚ источник (UI‚ API‚ JDBC)‚ длительность‚ строки‚ I/O статистику и профиль выполнения (Photon/Classic).
Сравнительная таблица методов архивирования
| Метод | Срок хранения | Детализация | Накладные расходы | Основной кейс |
|---|---|---|---|---|
| Query Store | Дни/Годы (настраиваемо) | Текст‚ План‚ Статистика (агрегаты по интервалам) | Низкие/Средние (~1-5% CPU) | Тюнинг производительности‚ регрессия планов‚ Force Plan |
| sys.dm_exec_query_stats | До перезапуска/вытеснения | Агрегаты по кэшу планов | Нулевые | Быстрая диагностика «здесь и сейчас» |
| Extended Events | До ротации файлов (ГБ/ТБ) | Полный текст‚ параметры‚ контекст‚ wait stats | Низкие (при правильной настройке) | Аудит безопасности‚ глубокая отладка‚ баг-репорты |
| SQL Audit | По политике хранения | Кто/Что/Когда (Action Groups) | Средние | Compliance‚ SOX‚ GDPR |
| Transaction Log | До бэкапа лога / Simple Recovery | Только DML изменения (физические операции) | Нулевые (всегда включен) | Point-in-time recovery‚ аудит изменений данных (CDC) |
| Client Tools (DataGrip‚ SSMS+Plugins) | Локально (настройки IDE) | То‚ что послал клиент | Нулевые для сервера | Личная история разработчика‚ восстановление потерянного скрипта |
Лучшие практики (Best Practices)
- Включайте Query Store на всех продакшн-базах. Это стандарт де-факто. Установите
OPERATION_MODE = READ_WRITE‚CLEANUP_POLICY(например‚ 30-90 дней)‚MAX_STORAGE_SIZE_MB(1-10% от размера БД). - Настройте Extended Events сессию для аудита ошибок и долгих запросов. Например‚ логгируйте
sql_batch_completedWHEREduration > 5000000(5 сек). Это даст детализацию‚ которой нет в агрегатах Query Store. - Не полагайтесь только на sys.dm_db_index_usage_stats для аудита доступа. Он сбрасывается при рестарте. Для критичных таблиц настройте Audit или XEvent на
SELECT. - Автоматизируйте очистку. Query Store имеет встроенную очистку. Для XEvent файлов настройте
max_file_sizeиmax_rollover_files. Для логов клиентских инструментов — лимиты размера папки логов. - Используйте параметризацию. Query Store и кэш планов лучше работают с параметризованными запросами. Ad-hoc запросы с литералами загрязняют архив дубликатами.
Типичные сценарии расследования с использованием архива
Сценарий А: «Запрос работал быстро вчера‚ сегодня тормозит»
- Откройте Query Store -> Regressed Queries (Регрессировавшие запросы).
- Найдите Query ID по времени или тексту.
- Посмотрите на графике появление нового Plan ID (точка на графике смены цвета/формы).
- Сравните планы (старый быстрый vs новый медленный) -> Причина: отсутствие индекса‚ устаревшая статистика‚ parameter sniffing.
- Примените Force Plan на старый хороший план как временное решение.
Сценарий Б: «Кто удалил данные из таблицы X в 14:30?»
- Query Store не поможет (он не логгирует значения параметров DML по умолчанию‚ только агрегаты).
- Проверьте SQL Server Audit (если настроен на DELETE для этой таблицы).
- Если Audit нет — анализируйте Transaction Log через
fn_dblogили сторонние инструменты (ApexSQL Log‚ SQL Log Rescue) по LSN / времени. - Проверьте Extended Events сессию (если ловили
sql_batch_completedс фильтром по тексту).
Сценарий В: «Нужно восстановить запрос‚ который я писал 3 дня назад в SSMS и закрыл без сохранения»
- Если стоит SSMS Tools Pack -> Меню SSMS Tools -> Query History.
- Если пользуетесь DataGrip -> Help -> Show SQL Log -> Поиск по времени/тексту.
- Если ничего не стоит — шансы минимальны (только если запрос попал в Query Store / кэш планов и не вытеснился).
Грамотная организация архива выполненных запросов — это многослойная система защиты и диагностики. Базовым слоем должен быть Query Store (включен по умолчанию в Azure SQL‚ обязателен к включению в On-Prem). Слойом аудита и детального расследования, Extended Events и SQL Audit. Слойом личной продуктивности разработчика — история в IDE (DataGrip‚ SSMS+Plugins). Облачные платформы (Fabric‚ Databricks) абстрагируют сложность‚ предоставляя единый UI Query Activity.
Инвестиции в настройку этих механизмов окупаются с первой же инцидентной ситуацией‚ когда вопрос «Что произошло?» получает точный‚ основанный на данных ответ за минуты‚ а не часы гаданий.
Статья подготовлена на основе актуальной документации Microsoft Learn (SQL Server‚ Azure SQL‚ Fabric)‚ практик сообщества SQL.ru‚ Stack Overflow и опыта администрирования высоконагруженных систем. Дата актуальности: .