Какую информацию о событиях я могу получить по умолчанию из SQL Server?

60

Я часто вижу вопросы, где люди хотят знать, произошла ли определенная вещь, или когда это произошло, или кто выполнил действие. Во многих случаях SQL Server просто не отслеживает эту информацию самостоятельно. Например:

  • Кто последний выполнял хранимую процедуру dbo.MyProcedure?
  • Кто обновил salaryколонку в dbo.Employeesтаблице?
  • Кто последний раз запрашивал dbo.Ordersтаблицу из Management Studio?

Но есть несколько других событий, SQL - сервер делает отслеживать временно по умолчанию, и может напрямую отвечать на вопросы, такие как:

  • Когда в последний раз происходило автоматическое увеличение базы данных AdventureWorks, и сколько времени это заняло?
  • Кто удалил dbo.EmployeeAuditDataтаблицу и когда?
  • Сколько ошибок, связанных с памятью, произошло сегодня?

Как получить эту информацию и как долго она остается доступной?

Аарон Бертран
источник

Ответы:

65

Существует довольно много ценной информации, которую SQL Server отслеживает для вас по умолчанию. Начиная с SQL Server 2005 в фоновом режиме выполнялась «трассировка по умолчанию», а с SQL Server 2008 автоматически запускался сеанс расширенных событий system_health.

Вы также можете найти определенную информацию из журнала SQL Server ошибок, журнал агента SQL Server, журналы событий Windows, а также дополнительные протоколирования от таких вещей , как аудит SQL Server , управления хранилища данных , уведомления о событиях , DML Триггеры , DDL триггеры , SCOM / System Center ваши собственные серверные трассировки или сеансы расширенных событий или сторонние решения для мониторинга (например, разработанные моим работодателем, SQL Sentry ). Вы также можете дополнительно включить так называемую «трассировку черного ящика», чтобы помочь в устранении неполадок .

Но в этом посте я собираюсь сосредоточить внимание на вещах, которые, как правило, включены чаще всего повсюду: трассировка по умолчанию, сеансы расширенных событий и журнал ошибок.

Трассировка по умолчанию

Трассировка по умолчанию обычно выполняется в большинстве систем, если вы не отключили ее с помощьюsp_configure . Пока он включен, это может быть богатым источником ценной информации. Ниже перечислены события трассировки, которые фиксируются:

DECLARE @TraceID INT;

SELECT @TraceID = id FROM sys.traces WHERE is_default = 1;

SELECT t.EventID, e.name as Event_Description
  FROM sys.fn_trace_geteventinfo(@TraceID) t
  JOIN sys.trace_events e ON t.eventID = e.trace_event_id
  GROUP BY t.EventID, e.name;

Вы можете получить более подробную информацию, присоединившись к, sys.trace_columnsчтобы увидеть, какие события и с какими данными приходят, но я пока пропущу это, поскольку вы можете увидеть, что у вас есть, когда вы фактически запрашиваете данные трассировки для конкретных событий. Это события, которые доступны в моей системе (чтобы убедиться, что они совпадают, вы должны выполнить запрос в своей системе, хотя это все тот же набор событий через SQL Server 2019 CTP 2.4):

EventID  Event_Description
-------  ----------------------------------------------
18       Audit Server Starts And Stops
20       Audit Login Failed
22       ErrorLog
46       Object:Created
47       Object:Deleted
55       Hash Warning
69       Sort Warnings
79       Missing Column Statistics
80       Missing Join Predicate
81       Server Memory Change
92       Data File Auto Grow
93       Log File Auto Grow
94       Data File Auto Shrink
95       Log File Auto Shrink
102      Audit Database Scope GDR Event
103      Audit Schema Object GDR Event
104      Audit Addlogin Event
105      Audit Login GDR Event
106      Audit Login Change Property Event
108      Audit Add Login to Server Role Event
109      Audit Add DB User Event
110      Audit Add Member to DB Role Event
111      Audit Add Role Event
115      Audit Backup/Restore Event
116      Audit DBCC Event
117      Audit Change Audit Event
152      Audit Change Database Owner
153      Audit Schema Object Take Ownership Event
155      FT:Crawl Started
156      FT:Crawl Stopped
164      Object:Altered
167      Database Mirroring State Change
175      Audit Server Alter Trace Event
218      Plan Guide Unsuccessful

Обратите внимание, что в трассировке по умолчанию используются файлы ролловеров, и поэтому доступные вам данные будут возвращаться только до настоящего времени - диапазон дат доступных данных зависит от того, сколько из указанных событий регистрируется и с какой периодичностью. Если вы хотите сохранить более длинную историю, вы можете настроить задание, которое периодически архивирует текущие неактивные файлы, связанные с трассировкой.

Примеры

В вопросе я задал пару вопросов, которые нашел. Вот примеры запросов для извлечения этой конкретной информации из трассировки по умолчанию.

Вопрос: Когда в последний раз происходило автоматическое увеличение базы данных AdventureWorks, и сколько времени это заняло?

Этот запрос извлекает все события AutoGrow в базе данных AdventureWorks как для файлов журнала, так и для файлов данных, которые все еще находятся в файлах журнала трассировки по умолчанию:

DECLARE @path NVARCHAR(260);

SELECT 
   @path = REVERSE(SUBSTRING(REVERSE([path]), 
   CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM    sys.traces
WHERE   is_default = 1;

SELECT 
   DatabaseName,
   [FileName],
   SPID,
   Duration,
   StartTime,
   EndTime,
   FileType = CASE EventClass WHEN 92 THEN 'Data' ELSE 'Log' END
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass IN (92,93)
AND DatabaseName = N'AdventureWorks'
ORDER BY StartTime DESC;

Вопрос: Кто и когда удалил таблицу dbo.EmployeeAuditData?

Это вернет любые DROPсобытия для объекта с именем EmployeeAuditData. Если вы хотите убедиться, что он обнаруживает только DROPсобытия для таблиц, вы можете добавить фильтр: ObjectType = 8277( полный список приведен здесь ). Если вы хотите , чтобы ограничить область поиска для конкретной базы данных, вы можете добавить фильтр: DatabaseName = N'db_name'.

DECLARE @path NVARCHAR(260);

SELECT 
   @path = REVERSE(SUBSTRING(REVERSE([path]), 
   CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM    sys.traces
WHERE   is_default = 1;

SELECT 
  LoginName,
  HostName,
  StartTime,
  ObjectName,
  TextData
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass = 47    -- Object:Deleted
AND EventSubClass = 1
AND ObjectName = N'EmployeeAuditData'
ORDER BY StartTime DESC;

Здесь есть осложнение, и это очень серьезный случай, но я все равно решил упомянуть об этом. Если вы используете несколько схем и можете иметь одно и то же имя объекта в нескольких схемах, вы не сможете определить, какая из них (если его аналоги все еще существуют). Существует внешний случай, когда UserA мог отбросить SchemaB.Tablename, в то время как UserB мог отбросить SchemaA.Tablename. Трассировка по умолчанию не отслеживает схему объекта (и при этом не захватывает TextDataдля этого события), иObjectIDвключение в трассировку бесполезно для прямого сопоставления (поскольку объект был отброшен и больше не существует). Включение этого столбца в выходные данные в этом случае может быть полезно для перекрестной ссылки на любые копии таблицы с тем же именем, которые все еще существуют, но если система находится в таком большом беспорядке (или если все такие копии были удалены) все еще не может быть надежным способом угадать, какая копия таблицы была кем отброшена.

Расширенные события

Из раздела Поддержка SQL Server 2008: сеанс system_health (блог SQLCSS) ниже приведен список данных, которые можно отбирать из system_healthсеанса в SQL Server 2008 и 2008 R2:

  • Sql_text и session_id для любых сессий, которые сталкиваются с ошибкой с серьезностью> = 20
  • Sql_text и session_id для любых сессий, которые встречаются с ошибкой типа «память», такой как 17803, 701 и т. Д. (Мы добавили это, потому что не все ошибки памяти имеют серьезность> = 20)
  • Запись о любых "не приносящих доход" проблемах (вы иногда видели их в ОШИБКЕ как Msg 17883)
  • Все обнаруженные тупики
  • Callstack, sql_text и session_id для любых сессий, которые ждали на защелках (или других интересных ресурсах) в течение> 15 секунд
  • Callstack, sql_text и session_id для любых сессий, которые ждали блокировок> 30 секунд
  • Callstack, sql_text и session_id для любого сеанса, который ожидал в течение расширенного периода времени для «внешних» ожиданий или «упреждающих ожиданий».

В разделе Использование сеанса событий system_health (MSDN) список несколько расширен в SQL Server 2012 (и остается неизменным для SQL Server 2014):

  • Sql_text и session_id для любых сеансов, в которых возникла ошибка с серьезностью> = 20.
  • Sql_text и session_id для любых сессий, которые сталкиваются с ошибкой, связанной с памятью. Ошибки включают 17803, 701, 802, 8645, 8651, 8657 и 8902.
  • Запись о любых невыдающихся проблемах планировщика. (Они появляются в журнале ошибок SQL Server как ошибка 17883.)
  • Любые обнаруженные тупики.
  • Callstack, sql_text и session_id для любых сессий, которые ожидали защелок (или других интересных ресурсов) в течение> 15 секунд.
  • Callstack, sql_text и session_id для любых сеансов, которые ожидали блокировок в течение> 30 секунд.
  • Callstack, sql_text и session_id для любых сеансов, которые долгое время ожидали упреждающего ожидания. Продолжительность зависит от типа ожидания. Превентивное ожидание - это когда SQL Server ожидает внешних вызовов API.
  • Callstack и session_id для распределения CLR и сбоев виртуального выделения.
  • События ring_buffer для брокера памяти, монитора планировщика, OOM узла памяти, безопасности и подключения.
  • Системный компонент является результатом sp_server_diagnostics.
  • Состояние экземпляра собрано scheduler_monitor_system_health_ring_buffer_recorded.
  • Ошибки распределения CLR.
  • Ошибки подключения с использованием connectivity_ring_buffer_recorded.
  • Ошибки безопасности с использованием security_error_ring_buffer_recorded.

В SQL Server 2016 фиксируются еще два события:

  • Когда процесс убит с помощью KILLкоманды.
  • Когда завершение работы SQL Server было начато.

(Документация еще не была обновлена, но я написал в блоге о том, как я обнаружил эти и другие изменения .)

Чтобы получить более загадочную конфигурацию, подходящую для вашей конкретной версии, вы всегда можете выполнить следующий запрос напрямую, но вам придется интерпретировать имена и анализировать предикаты, чтобы они соответствовали приведенным выше спискам более естественных языков:

SELECT e.package, e.event_id, e.name, e.predicate
  FROM sys.server_event_session_events AS e
  INNER JOIN sys.server_event_sessions AS s
  ON e.event_session_id = s.event_session_id
 WHERE s.name = N'system_health'
 ORDER BY e.package, e.name;

Если вы используете группы доступности, вы также найдете два новых сеанса: AlwaysOn_failoverи AlwaysOn_health. Вы можете увидеть данные, которые они собирают с помощью следующего запроса:

SELECT s.name, e.package, e.event_id, e.name, e.predicate
  FROM sys.server_event_session_events AS e
  INNER JOIN sys.server_event_sessions AS s
  ON e.event_session_id = s.event_session_id
 WHERE s.name LIKE N'AlwaysOn[_]%'
 ORDER BY s.name, e.package, e.name;

Эти сеансы событий используют цели кольцевого буфера для хранения данных, так что, как и пул буферов и кэш плана, более старые события будут поэтапно исключены, поэтому вы не обязательно сможете извлекать события из желаемого диапазона дат.

пример

В вопросе я поставил этот вымышленный вопрос:

Сколько ошибок, связанных с памятью, произошло сегодня?

Вот пример (и, вероятно, не очень эффективный) запрос, который может извлечь эту информацию из system_healthсеанса:

;WITH src(x) AS
(
  SELECT y.query('.')
  FROM
  (
    SELECT x = CONVERT(XML, t.target_data)
      FROM sys.dm_xe_sessions AS s
      INNER JOIN sys.dm_xe_session_targets AS t
      ON s.[address] = t.event_session_address
      WHERE s.name = N'system_health'
  ) AS x
  CROSS APPLY x.x.nodes('/RingBufferTarget/event') AS y(y)
)
SELECT 
  x, ts = CONVERT(DATETIME, NULL), err = CONVERT(INT, NULL)
INTO #blat FROM src;

DELETE #blat WHERE x.value('(/event/@name)[1]', 'varchar(255)') <> 'error_reported';

UPDATE #blat SET ts = x.value('(/event/@timestamp)[1]', 'datetime');

UPDATE #blat SET err = x.value('(/event/data/value)[1]', 'int');

SELECT err, number_of_events = COUNT(*)
  FROM #blat
  WHERE err IN (17803, 701, 802, 8645, 8651, 8657, 8902)
  AND ts >= CONVERT(DATE, CURRENT_TIMESTAMP)
  GROUP BY err;

DROP TABLE #blat;

(Этот пример заимствован из вступительного сообщения Амит Банерджи в блоге о system_healthсеансе .)

Для получения дополнительной информации о расширенных событиях (включая множество примеров, в которых вы можете запрашивать конкретные данные), см. Эту серию из 31 статей, написанную Джонатаном Кехайясом:

https://www.sqlskills.com/blogs/jonathan/an-xevent-a-day-31-days-of-extended-events/

Журнал ошибок

SQL Server по умолчанию сохраняет текущие плюс 6 самых последних файлов журнала ошибок (но вы можете изменить это ). Там хранится много информации, включая информацию о запуске (сколько ядер используется, установлена ​​ли блокировка страниц в памяти, режим аутентификации и т. Д.), А также об ошибках и других сценариях, достаточно серьезных, чтобы их можно было документировать (и не фиксировать в другом месте). Одним из недавних примеров был тот, кто искал, когда база данных была отключена. Вы можете определить это, просматривая каждый из последних 7 журналов ошибок для текста Setting database option OFFLINE:

EXEC sys.sp_readerrorlog 0,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 1,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 2,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 3,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 4,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 5,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 6,1,'Setting database option OFFLINE';

В этом недавнем ответе я рассказал о некоторых других деталях , а также о хорошей исходной информации в toadworld, а также в официальной документации .

Одна группа «ошибок», которую журнал ошибок отслеживает по умолчанию - и которая может значительно ускорить выпадение важной информации, - это каждое успешное сообщение резервного копирования. Вы можете предотвратить их заполнение журнала ошибок шумом, включив флаг трассировки 3226 .

Аарон Бертран
источник