web-dev-qa-db-ja.com

SQL Serverからデフォルトで取得できるイベント情報は何ですか?

特定のことが起こったのか、いつ起こったのか、誰がそのアクションを実行したのかを人々が知りたがっている質問をよく目にします。多くの場合、SQL Serverはこの情報を単独で追跡しません。例えば:

  • ストアドプロシージャdbo.MyProcedureを最後に実行したのは誰ですか?
  • dbo.Employeesテーブルのsalary列を更新したのは誰ですか?
  • Management Studioからdbo.Ordersテーブルを最後にクエリしたのは誰ですか?

しかし、SQL Server 行うがデフォルトで一時的に追跡する他のイベントがいくつかあり、次のような質問にネイティブに回答できます。

  • 自動拡張がAdventureWorksデータベースで最後に発生したのはいつで、どのくらい時間がかかりましたか?
  • 誰がdbo.EmployeeAuditDataテーブルをいつ削除しましたか?
  • 今日、メモリ関連のエラーはいくつ発生しましたか?

この情報を取得するにはどうすればよいですか?

61
Aaron Bertrand

SQL Serverがデフォルトで追跡する貴重な情報がかなりあります。 SQL Server 2005以降、バックグラウンドで実行される「デフォルトトレース」があり、SQL Server 2008以降、system_healthと呼ばれる拡張イベントセッションが自動的に実行されています。

SQL Serverエラーログ、SQL Serverエージェントログ、Windowsイベントログ、および SQL Server AuditManagement Data Warehouse などの追加のログから特定の情報を見つけることもできます。 、 イベント通知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データベースで最後に自動拡張が行われたのはいつで、どのくらい時間がかかりましたか?

このクエリは、ログファイルとデータファイルの両方について、AdventureWorksデータベース内のすべてのAutoGrowイベントをプルします。これらは、デフォルトのトレースログファイルに残っています。

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を削除したという外部のケースがあります。デフォルトのトレースはオブジェクトのschemaを追跡しません(また、このイベントのTextDataをキャプチャしません)、およびに含まれるObjectID traceは直接一致には役立ちません(オブジェクトが削除され、存在しなくなったため)。この場合、出力にその列を含めることは、まだ存在する同じ名前のテーブルのコピーに対して相互参照するのに役立つ可能性がありますが、システムがこれほど混乱している場合(またはそのようなコピーがすべて削除されている場合)それでも、誰がテーブルのどのコピーを削除したかを推測するための信頼できる方法ではない可能性があります。

拡張イベント

SQL Server 2008のサポート:system_healthセッション(SQLCSSブログ) から、SQL Server 2008および2008 R2のsystem_healthセッションからカリングできるデータのリストは次のとおりです。

  • 重大度> = 20のエラーが発生したセッションのsql_textおよびsession_id
  • 17803、701などの「メモリ」タイプのエラーが発生したセッションのsql_textおよびsession_id(すべてのメモリエラーが重大度> = 20ではないため、これを追加しました)
  • 「譲歩しない」問題の記録(ERRORLOGでメッセージ17883として見られることがあります)
  • 検出されたデッドロック
  • ラッチ(または他の興味深いリソース)で15秒以上待機したセッションの呼び出しスタック、sql_text、およびsession_id
  • ロックで30秒以上待機したセッションの呼び出しスタック、sql_text、およびsession_id
  • 「外部」待機または「プリエンプティブ待機」を長時間待機したセッションの呼び出しスタック、sql_text、およびsession_id。

system_healthイベントセッション(MSDN)を使用 から、SQL Server 2012ではリストが多少拡張されています(SQL Server 2014でも同じです)。

  • 重大度が20以上のエラーが発生したセッションのsql_textおよびsession_id。
  • メモリ関連のエラーが発生したセッションのsql_textおよびsession_id。エラーには、17803、701、802、8645、8651、8657、および8902が含まれます。
  • 降伏しないスケジューラーの問題の記録。 (これらは、SQL Serverエラーログにエラー17883として表示されます。)
  • 検出されたデッドロック。
  • ラッチ(またはその他の興味深いリソース)で15秒以上待機したセッションの呼び出しスタック、sql_text、およびsession_id。
  • ロックで30秒以上待機したすべてのセッションの呼び出しスタック、sql_text、およびsession_id。
  • プリエンプティブな待機を長時間待機したセッションの呼び出しスタック、sql_text、およびsession_id。期間は待機タイプによって異なります。プリエンプティブ待機は、SQL Serverが外部API呼び出しを待機している場所です。
  • CLR割り当ておよび仮想割り当ての失敗に対するcallstackおよびsession_id。
  • メモリブローカー、スケジューラーモニター、メモリノードのOOM、セキュリティ、接続のring_bufferイベント。
  • システムコンポーネントは、sp_server_diagnosticsの結果です。
  • Scheduler_monitor_system_health_ring_buffer_recordedによって収集されたインスタンスの状態。
  • CLR割り当てエラー。
  • Connectivity_ring_buffer_recordedを使用した接続エラー。
  • Security_error_ring_buffer_recordedを使用したセキュリティエラー。

SQL Server 2016では、さらに2つのイベントがキャプチャされます。

  • 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_failoverAlwaysOn_healthの2つの新しいセッションも実行されます。次のクエリを使用して、彼らが収集したデータを確認できます。

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セッションでのAmit Banerjeeの紹介ブログ投稿 から大まかに借りています。)

拡張イベント(特定のデータをクエリできる多くの例を含む)の詳細については、Jonathan Kehayiasによる次の31部構成のブログシリーズを参照してください。

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

エラーログ

SQL Serverはデフォルトで、現在と最新の6つのエラーログファイルを保持します(ただし これは変更できます )。起動情報(使用中のコアの数、メモリ内のロックページが設定されているかどうか、認証モードなど)や、文書化できるほど深刻なエラーやその他のシナリオ(他の場所ではキャプチャされない)など、多くの情報がそこに保存されます。最近の例の1つは、データベースがオフラインになったときに探している人でした。テキストSetting database option OFFLINEの最新の7つのエラーログをそれぞれスキャンすることで、これを確認できます。

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で および 公式ドキュメントで もあります。

デフォルトでエラーログが追跡する「エラー」の1つのグループは、重要な情報が尾からすぐに落ちる可能性があります-すべての成功したバックアップメッセージです。 トレースフラグ3226を有効にする を使用すると、エラーログがノイズでいっぱいになるのを防ぐことができます。

66
Aaron Bertrand