[ Partie 1 | Partie 2 | Partie 3 ]
Dans la partie 1 de cette série, j'ai expliqué comment je suis arrivé à la conclusion que nous devrions désactiver la trace par défaut. Dans la partie 2, j'ai montré la session d'événements étendus que j'ai déployée pour capturer tous les événements de changement de taille de fichier. Dans cet article, je veux montrer les vues que j'ai créées pour faciliter la consommation des données d'événement pour les gens, et aussi pour expliquer certaines mises en garde.
Vues digestes
Tout d'abord, j'ai créé une vue qui exposerait les éléments importants des données de session des événements étendus et les placerait dans la base de données d'utilitaires qui existe sur chaque instance :
CREATE VIEW dbo.vwFileSizeChanges AS WITH FileInfo(XEPath) AS ( SELECT LEFT(BasePath,COALESCE(NULLIF(CHARINDEX(SessName,BasePath)-1,-1),0)) + SessName + N'*.xel' FROM ( SELECT xmlsrc.data.value(N'(@name)[1]', N'nvarchar(max)'), SessName FROM ( SELECT CONVERT(xml,target_data), s.[name] FROM sys.dm_xe_session_targets AS t INNER JOIN sys.dm_xe_sessions AS s ON s.[address] = t.event_session_address WHERE s.[name] = N'FileSizeChanges' ) AS xefile (TargetData, SessName) CROSS APPLY TargetData.nodes(N'//EventFileTarget/File') AS xmlsrc(data) ) AS InnerData(BasePath, SessName) ), SessionData([EventData]) AS ( SELECT CONVERT(xml, TargetData.event_data) FROM FileInfo CROSS APPLY sys.fn_xe_file_target_read_file(FileInfo.XEPath, NULL, NULL, NULL) AS TargetData ), src AS ( SELECT EndTimeUTC = x.d.value(N'(@timestamp)[1]', N'datetime2'), DatabaseID = x.d.value(N'(data [@name="database_id"]/value)[1]', N'int'), [FileName] = x.d.value(N'(data [@name="file_name"]/value)[1]', N'sysname'), Duration = x.d.value(N'(data [@name="duration"]/value)[1]', N'int'), FileType = x.d.value(N'(data [@name="file_type"]/text)[1]', N'varchar(4)'), Culprit = x.d.value(N'(action[@name="sql_text"]/value)[1]', N'nvarchar(max)'), IsAutomatic = x.d.value(N'(data [@name="is_automatic"]/value)[1]', N'varchar(5)'), ChangeKB = x.d.value(N'(data [@name="size_change_kb"]/value)[1]', N'bigint'), Principal = x.d.value(N'(action[@name="server_principal_name"]/value)[1]', N'sysname'), username = x.d.value(N'(action[@name="username"]/value)[1]', N'sysname'), AppName = x.d.value(N'(action[@name="client_app_name"]/value)[1]', N'sysname'), HostName = x.d.value(N'(action[@name="client_hostname"]/value)[1]', N'sysname') --, [EventData] -- raw XML to troubleshoot specific events FROM SessionData CROSS APPLY EventData.nodes('/event') AS x(d) ) SELECT DatabaseName = DB_NAME(DatabaseID), [FileName], DurationSeconds = CONVERT(decimal(18,3),Duration/1000000.0), StartTimeUTC = CONVERT(datetime2(3), DATEADD(MICROSECOND, -Duration, EndTimeUTC)), EndTimeUTC = CONVERT(datetime2(3), EndTimeUTC), FileType, Culprit = CASE WHEN Culprit IS NULL AND AppName LIKE N'Repl%' THEN AppName ELSE Culprit END, IsAutomatic, ChangeMB = CONVERT(decimal(18,3), ChangeKB / 1024.0), Principal = COALESCE([Principal], COALESCE(NULLIF(username,N''),N'?')), HostName, App = CASE WHEN AppName LIKE N'%Management Studio%Query%' THEN N'SSMS - Query Window' WHEN AppName LIKE N'%Management Studio%' THEN N'SSMS - GUI!' ELSE AppName END--, [EventData] -- raw XML to troubleshoot specific events FROM src;
Désormais, lorsque quelqu'un souhaite consulter les événements récents de modification de la taille des fichiers sur n'importe quel serveur, il exécute :
SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges ORDER BY StartTimeUTC DESC;
Lorsque vous désactivez la trace par défaut, les fichiers de trace ne sont pas supprimés, de sorte que tous les événements antérieurs à cette modification sont toujours révisables. Je peux emprunter au fait que la trace par défaut est codée en dur sur le même chemin que SERVERPROPERTY(N'ErrorLogFileName')
, et créez une deuxième vue qui réunit les données ci-dessus avec plus de données de la trace par défaut :
CREATE VIEW dbo.vwFileSizeChanges_IncludingTrace AS WITH dst AS ( SELECT s,e,d FROM (VALUES ('20190310','20191103',240),('20191103','20200308',300), ('20200308','20201101',240),('20201101','20210314',300), ('20210314','20211107',240)) AS dst(s,e,d) -- arbitrary date range to support DST conversions going a year+ each way -- will add 2022, 2023, etc. later (if DST is still a thing then) ),vars(TracePath) AS ( SELECT REVERSE(SUBSTRING(p, CHARINDEX(N'\', p), 260)) + N'log.trc' FROM (SELECT REVERSE((CONVERT(nvarchar(max), SERVERPROPERTY(N'ErrorLogFileName'))))) AS s(p) ), trc AS ( SELECT t.DatabaseName, t.[FileName], DurationSeconds = CONVERT(decimal(18,3), t.Duration/1000000.0), StartTimeUTC = CONVERT(datetime2(3), DATEADD(MINUTE, COALESCE(st1.d,0), t.StartTime)), EndTimeUTC = CONVERT(datetime2(3), DATEADD(MINUTE, COALESCE(st2.d,0), t.EndTime)), FileType = CASE WHEN t.EventClass IN (92, 94) THEN 'Data' WHEN t.EventClass IN (93, 95) THEN 'Log' END, Culprit = CASE WHEN t.TextData IS NULL AND t. ApplicationName LIKE N'Repl%' THEN t.ApplicationName ELSE t.TextData END, IsAutomatic = 'true', ChangeMB = CONVERT(bigint, t.IntegerData)*8/1024, Principal = t.LoginName, t.HostName, App = CASE WHEN t.ApplicationName LIKE N'%Management Studio%Query%' THEN N'SSMS - Query Window' WHEN t.ApplicationName LIKE N'%Management Studio%' THEN N'SSMS - GUI!' ELSE t.ApplicationName END --, [EventData] = CONVERT(xml, NULL) FROM vars CROSS APPLY sys.fn_trace_gettable(vars.TracePath, DEFAULT) AS t LEFT OUTER JOIN dst AS st1 ON t.StartTime >= DATEADD(HOUR,2,st1.s) AND t.StartTime < DATEADD(HOUR,2,st1.e) LEFT OUTER JOIN dst AS st2 ON t.EndTime >= DATEADD(HOUR,2,st2.s) AND t.EndTime < DATEADD(HOUR,2,st2.e) WHERE t.EventClass IN (92,93) ) SELECT src='trace', * FROM trc UNION ALL SELECT src='xe', * FROM dbo.vwFileSizeChanges;
Cette vue ajuste les données de trace (capturées à l'heure de l'Est sur tous nos serveurs) à l'UTC et gère également l'heure d'été le cas échéant. Si les données se situent en dehors de la plage du CTE appelé dst
, il sera exprimé en heure de l'Est à la place (et vous pouvez facilement résoudre ce problème en ajoutant plus de plages d'heure d'été). Il y a une colonne supplémentaire appelée src
afin que vous puissiez interroger les anciennes données de trace en utilisant :
SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges_IncludingTrace WHERE src = 'trace' ORDER BY StartTimeUTC DESC;
Mises en garde
Il n'y a pas de repas gratuit ! Bien que je sois convaincu que la suppression de la trace par défaut n'aura aucun impact ou, bien plus probablement, un impact positif sur nos charges de travail, il y a certaines choses à garder à l'esprit pour votre environnement si vous choisissez de suivre mon chemin :
- Les bases de données ne sont pas permanentes
Dans ma définition de session Extended Events, j'ai choisi de ne pas implémenter
collect_database_name
, et dans la vue ci-dessus, vous pouvez voir que je résous ce problème lors de l'exécution en utilisantDB_NAME(database_id)
. Il y a un risque ici, dans la mesure où quelqu'un pourrait créer une base de données, effectuer un tas d'activités qui créent un roulement de fichiers et un vidage de disque, puis supprimer la base de données. Ledatabase_id
exposé dans le XML n'a plus de sens dans ce cas, etDB_NAME()
renverraNULL
.J'ai choisi ce résultat plutôt que de me fier uniquement au nom de la base de données, car la chaîne d'événements ci-dessus est beaucoup moins probable qu'un changement de nom de base de données (où un
database_id
restera le même). Dans ce cas, vous recherchez peut-être des événements qui se sont produits dans une base de données, mais en utilisant le mauvais nom (actuel), selon le moment où les événements se sont produits.Si vous souhaitez pouvoir utiliser l'un ou l'autre, vous pouvez utiliser à la place la définition de session suivante :
... ADD EVENT sqlserver.database_file_size_change ( SET collect_database_name = (1) ACTION ( sqlserver.sql_text, ...
Cela ne peut pas être gratuit non plus, ou cela se produirait par défaut, mais j'avoue que je n'ai jamais testé l'impact de l'ajout de cela à la collection.
- Les rapports SSMS seront légèrement moins fiables
Un effet secondaire de la désactivation de la trace par défaut perturbe certains des « rapports standard » de Management Studio. J'ai interrogé nos équipes avant de faire cela, et vous voudrez faire de même pour vous assurer que vos utilisateurs ne comptent sur aucun de ces éléments. Vous voudrez également leur rappeler que les rapports ne sont actuellement pas fiables de toute façon, pour la même raison que je ne peux pas compter directement sur la trace par défaut - ils ne peuvent extraire que les données qui sont encore dans la trace. Un rapport vide ne signifie pas nécessairement qu'aucun événement ne s'est produit; cela pourrait simplement signifier que l'information n'est plus disponible. Si c'est vraiment là qu'une équipe souhaite utiliser ces informations, je peux m'assurer qu'elles sont fiables en leur envoyant des rapports personnalisés qui utilisent une source plus fiable.
Les rapports suivants sont ceux que j'ai pu voir dériver au moins certaines de leurs informations de la trace par défaut, et pourquoi nous n'avons pas besoin des rapports même s'ils peuvent être fiables :
Historique des modifications de schéma Nous avons déjà mis en place un contrôle des sources, un processus de révision/déploiement rigoureux et des déclencheurs DDL qui capturent les informations sur les modifications de schéma. Historique des modifications de configuration
et
Consommation de mémoireNotre outil de surveillance nous informe des changements de configuration au niveau de l'instance, ce rapport dans SSMS est donc redondant. Échecs de connexion Ceux-ci se trouvent dans le journal des erreurs (et dans l'observateur d'événements) car, en standard, nous activons l'audit "Échecs de connexion uniquement" pour toutes les instances SQL Server. Certains serveurs disposent également d'un audit formel supplémentaire pour des raisons de conformité. Utilisation du disque Entre autres informations, cela répertorie les événements de croissance automatique et de réduction automatique de la trace par défaut, que nous capturons désormais à l'aide d'événements étendus. Événements de sauvegarde et de restauration Ces informations sont facilement disponibles dans msdb.dbo.backupset si nous en avons besoin, mais elles sont également intégrées à notre automatisation autour de la sauvegarde et de la restauration (nous ne regardons jamais la trace par défaut pour ces informations).
Historique de cohérence de la base de données Comme pour les sauvegardes, nous avons une automatisation construite autour de DBCC CHECKDB ; si quelqu'un est sorti de cela et a exécuté quelque chose manuellement, cela apparaîtra toujours dans le journal des erreurs. Et nous avons beaucoup plus de contrôle sur la durée de conservation des journaux d'erreurs et sur la fréquence à laquelle nous les recyclons. Nous recyclons tous les soirs afin qu'il soit plus facile de trouver un événement que nous soupçonnons de s'être produit un jour donné dans le passé.
Conclusion
C'était un projet amusant mais compliqué, et je suis satisfait du résultat jusqu'à présent. Merci d'avoir roulé !
[ Partie 1 | Partie 2 | Partie 3 ]