Je vois souvent des questions où les gens veulent savoir si une certaine chose s'est produite, ou quand elle s'est produite, ou qui a effectué l'action. Dans de nombreux cas, SQL Server ne suit tout simplement pas ces informations de lui-même. Par exemple:
dbo.MyProcedure
?salary
dans le dbo.Employees
table?dbo.Orders
table de Management Studio?Mais il existe plusieurs autres événements que SQL Server fait suit temporairement par défaut et peut répondre de manière native aux questions, telles que:
dbo.EmployeeAuditData
table et quand?Comment puis-je obtenir ces informations et combien de temps restent-elles disponibles?
Il y a pas mal d'informations précieuses que SQL Server suit pour vous par défaut. Depuis SQL Server 2005, il existe une "trace par défaut" qui s'exécute en arrière-plan, et depuis SQL Server 2008, une session d'événements étendus s'exécutant automatiquement, appelée system_health
.
Vous pouvez également trouver certaines informations dans le journal des erreurs SQL Server, le journal de l'Agent SQL Server, les journaux des événements Windows et la journalisation supplémentaire à partir de choses comme Audit SQL Server , Management Data Warehouse , Notifications d'événements , Déclencheurs DML , Déclencheurs DDL , SCOM/System Center , vos propres traces côté serveur ou Sessions d'événements étendues ou solutions de surveillance tierces (comme celles faites par mon employeur, SQL Sentry ). Vous pouvez également éventuellement activer ne soi-disant "trace Blackbox" pour aider au dépannage .
Mais pour ce post, je vais me concentrer sur les choses qui sont généralement activées le plus partout: la trace par défaut, les sessions d'événements étendus et le journal des erreurs.
La trace par défaut s'exécute généralement sur la plupart des systèmes, sauf si vous avez désactivée à l'aide de sp_configure
. Tant qu'il est activé, cela peut être une riche source d'informations précieuses. La liste suivante répertorie les événements de trace capturés:
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;
Vous pouvez entrer dans les détails en vous joignant à sys.trace_columns
pour voir quels événements sont fournis avec quelles données, mais je vais ignorer cela pour l'instant, car vous pouvez voir ce que vous avez lorsque vous interrogez réellement les données de trace pour des événements spécifiques. Ce sont les événements disponibles sur mon système (vous devez exécuter la requête sur le vôtre pour vous assurer qu'ils correspondent, bien qu'il s'agisse toujours du même ensemble d'événements via 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
Notez que la trace par défaut utilise des fichiers de survol et que les données disponibles ne remontent que jusqu'à présent - la plage de dates des données disponibles dépend du nombre des événements ci-dessus qui sont capturés et à quelle fréquence. Si vous souhaitez vous assurer de conserver un historique plus long, vous pouvez configurer un travail qui archive périodiquement les fichiers actuellement inactifs associés à la trace.
Exemples
Dans la question, j'ai posé quelques questions que j'ai trouvées. Voici des exemples de requêtes pour extraire ces informations spécifiques de la trace par défaut.
Question: À quand remonte la dernière fois qu'une croissance automatique s'est produite dans la base de données AdventureWorks, et combien de temps a-t-elle pris?
Cette requête extrait tous les événements AutoGrow de la base de données AdventureWorks, pour les fichiers journaux et de données, qui se trouvent toujours dans les fichiers journaux de trace par défaut:
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;
Question: Qui a supprimé la table dbo.EmployeeAuditData et quand?
Cela renverra tous les événements DROP
pour un objet nommé EmployeeAuditData
. Si vous voulez vous assurer qu'il ne détecte que les événements DROP
pour les tables, vous pouvez ajouter un filtre: ObjectType = 8277
(le la liste complète est documentée ici ). Si vous souhaitez restreindre l'espace de recherche à une base de données spécifique, vous pouvez ajouter un filtre: 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;
Il y a une complication ici, et c'est très l'affaire Edge, mais j'ai pensé qu'il était prudent de mentionner quand même. Si vous utilisez plusieurs schémas et pouvez avoir le même nom d'objet dans plusieurs schémas, vous ne serez pas en mesure de dire lequel il s'agit (à moins que ses homologues existent toujours). Il existe un cas extérieur où UserA peut avoir supprimé SchemaB.Tablename tandis que UserB peut avoir supprimé SchemaA.Tablename. La trace par défaut ne suit pas le schéma de l'objet (pas plus qu'il ne capture TextData
pour cet événement), et le ObjectID
inclus dans la trace n'est pas utile pour une correspondance directe (car l'objet a été supprimé et n'existe plus). L'inclusion de cette colonne dans la sortie dans ce cas peut être utile pour effectuer une référence croisée avec toutes les copies de la table avec le même nom qui existent toujours, mais si le système est dans un tel désarroi (ou si toutes ces copies ont été supprimées) là peut-être pas encore un moyen fiable de deviner à quelle copie de la table a été déposée par qui.
De Prise en charge de SQL Server 2008: la session system_health (blog SQLCSS) , voici la liste des données que vous pouvez extraire du system_health
session dans SQL Server 2008 et 2008 R2:
De tiliser la session d'événement system_health (MSDN) , la liste est quelque peu développée dans SQL Server 2012 (et reste la même pour SQL Server 2014):
Dans SQL Server 2016, deux autres événements sont capturés:
KILL
.(La documentation n'a pas encore été mise à jour, mais j'ai blogué sur la façon dont je découvre ces changements et d'autres .)
Pour obtenir la configuration la plus cryptée applicable à votre version spécifique, vous pouvez toujours exécuter la requête suivante directement, mais vous devrez interpréter les noms et analyser les prédicats pour correspondre aux listes de langage plus naturel ci-dessus:
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;
Si vous utilisez des groupes de disponibilité, vous trouverez également deux nouvelles sessions en cours d'exécution: AlwaysOn_failover
et AlwaysOn_health
. Vous pouvez voir les données qu'ils collectent avec la requête suivante:
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;
Ces sessions d'événements utilisent des cibles de tampon en anneau pour stocker les données.Ainsi, comme le pool de tampons et le cache de plan, les événements plus anciens seront supprimés progressivement, vous ne pourrez donc pas nécessairement extraire les événements de la plage de dates souhaitée.
Exemple
Dans la question, j'ai posé cette question fictive:
Combien d'erreurs liées à la mémoire se sont produites aujourd'hui?
Voici un exemple de requête (et probablement pas très efficace) qui peut extraire ces informations de la system_health
session:
;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;
(Cet exemple emprunte librement à le billet de blog d'introduction d'Amit Banerjee sur le system_health
session .)
Pour plus d'informations sur les événements étendus (y compris de nombreux exemples où vous pouvez rechercher des données spécifiques), consultez cette série de blogs en 31 parties de Jonathan Kehayias:
https://www.sqlskills.com/blogs/jonathan/an-xevent-a-day-31-days-of-extended-events/
Par défaut, SQL Server conserve les fichiers journaux d'erreurs actuels et les 6 plus récents (mais vous pouvez modifier cela ). De nombreuses informations y sont stockées, y compris des informations de démarrage (combien de cœurs sont utilisés, si les pages de verrouillage en mémoire sont définies, le mode d'authentification, etc.) ainsi que des erreurs et d'autres scénarios suffisamment graves pour être documentés (et non capturés ailleurs). Un exemple récent était quelqu'un qui cherchait quand une base de données était mise hors ligne. Vous pouvez le déterminer en parcourant chacun des 7 journaux d'erreurs les plus récents pour le texte 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';
J'ai couvert quelques autres détails dans cette réponse récente , et il y a aussi de bonnes informations générales sur toadworld et aussi dans la documentation officielle .
Un groupe d '"erreurs" que le journal des erreurs suit par défaut - et peut faire chuter les informations importantes beaucoup plus rapidement - est chaque message de sauvegarde réussi. Vous pouvez empêcher ces derniers de remplir le journal des erreurs avec du bruit en activant l'indicateur de trace 3226 .