Événements étendus vs SQL Audit - implications sur les performances


8

Je voudrais configurer un type de système de piste d'audit sur ma base de données pour surveiller les UPDATE/INSERTdéclarations sur une table spécifique avec une activité très élevée. J'ai deux options devant moi: utiliser le système d'audit intégré de SQL Server ou aller avec des événements étendus.

Comme SQL Server Audit utilise en interne les événements étendus, je suppose qu'il y aura une sorte de surcharge lorsque j'utiliserai Audit au lieu des événements étendus directement.

Existe-t-il un moyen de faire des tests pour analyser le système qui affecte le plus le serveur? Si je pouvais savoir ce qui se passe réellement lors de la création d'une session XE, cela m'aiderait à analyser l'impact sur le serveur.

Nous avons considéré les déclencheurs et laissé de côté cette option en raison des frais généraux. Mais cela a juste été décidé sur la base d'informations provenant d'Internet.



Tous les événements capturés via un audit SQL ne sont pas accessibles via XEvents. SQL Audit utilise le même moteur que XEvents, mais ce sont des fonctionnalités distinctes.

Ouais, j'ai appris ça. Mais quand nous avons fait une sorte de test de charge (voir ci-dessous), nous avons observé que XE a plus de frais généraux que Audit. Si l'audit utilise XE en arrière-plan, avez-vous une idée de la raison pour laquelle cela entraîne plus de frais généraux?
karun_r

Réponses:


3

J'ai créé une plate-forme de test simple pour tester SQL Server Audit contre les déclencheurs et potentiellement d'autres options. Dans mes tests d'insertion de 1 million de lignes dans une table, j'ai obtenu respectivement 52, 67 et 159 secondes pour la ligne de base, SQL Audit et mon déclencheur:

Résultats de test

Maintenant, ce n'est pas particulièrement scientifique, mais cela vous offre potentiellement un moyen de comparer les approches. Jetez un œil au script, voyez s'il peut vous être utile:

USE master
GO

SET NOCOUNT ON
GO

IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'testAuditDb' )
ALTER DATABASE testAuditDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'testAuditDb' )
DROP DATABASE testAuditDb
GO


CREATE DATABASE testAuditDb
ON PRIMARY
( NAME = N'testAuditDb', FILENAME = N's:\temp\testAuditDb.mdf', SIZE = 1GB, MAXSIZE = UNLIMITED, FILEGROWTH = 128MB )
LOG ON 
( NAME = N'testAuditDb_log', FILENAME = N's:\temp\testAuditDb_log.ldf', SIZE = 100MB, MAXSIZE = 2048GB, FILEGROWTH = 128MB )
GO

ALTER DATABASE testAuditDb SET RECOVERY SIMPLE
GO



------------------------------------------------------------------------------------------------
-- Setup START
------------------------------------------------------------------------------------------------

USE testAuditDb
GO

CREATE SCHEMA auditSchema

-- Create a table
CREATE TABLE auditSchema.auditTable ( 
    rowId INT IDENTITY PRIMARY KEY, 
    someData UNIQUEIDENTIFIER DEFAULT NEWID(), 
    dateAdded DATETIME DEFAULT GETDATE(), 
    addedBy VARCHAR(30) DEFAULT SUSER_NAME(), 
    ts ROWVERSION 
)
GO


-- Setup END
------------------------------------------------------------------------------------------------



------------------------------------------------------------------------------------------------
-- Test 01 - Baseline START
-- Normal timing; no triggers or audits
------------------------------------------------------------------------------------------------


-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2

SET @startTime = SYSDATETIME()

WHILE @i < 1000000
BEGIN

    INSERT INTO auditSchema.auditTable DEFAULT VALUES

    SET @i += 1
END

SET @endTime = SYSDATETIME()

SELECT DATEDIFF( second, @startTime, @endTime ) AS baseline
GO


-- Cleanup
TRUNCATE TABLE auditSchema.auditTable 
GO

-- Test 01 - Baseline END
------------------------------------------------------------------------------------------------





------------------------------------------------------------------------------------------------
-- Test 02 - SQL Audit START
-- Try SQL Audit
------------------------------------------------------------------------------------------------

-- Create server audit in master database
USE master
GO

------------------------------------------------------------------------------------------------------------------------
-- The server audit is created with a WHERE clause that limits the server audit to only the auditTable table.
------------------------------------------------------------------------------------------------------------------------
CREATE SERVER AUDIT auditTableAccess TO FILE ( FILEPATH = 'S:\SQLAudit\' ) WHERE object_name = 'auditTable';
GO
ALTER SERVER AUDIT auditTableAccess WITH ( STATE = ON );
GO

-- Create the database audit specification in the testAuditDb database 
USE testAuditDb;
GO

CREATE DATABASE AUDIT SPECIFICATION [dbAudit1]
FOR SERVER AUDIT auditTableAccess
ADD ( 
    SELECT, INSERT, UPDATE ON SCHEMA::[auditSchema]
    BY [public]
    ) WITH ( STATE = ON );
GO


-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2

SET @startTime = SYSDATETIME()

WHILE @i < 1000000
BEGIN

    INSERT INTO auditSchema.auditTable DEFAULT VALUES

    SET @i += 1
END

SET @endTime = SYSDATETIME()

SELECT DATEDIFF( second, @startTime, @endTime ) AS sqlAudit
GO


-- Cleanup
TRUNCATE TABLE auditSchema.auditTable
GO
ALTER DATABASE AUDIT SPECIFICATION [dbAudit1] WITH ( STATE = Off );
DROP DATABASE AUDIT SPECIFICATION [dbAudit1]
GO

USE master
ALTER SERVER AUDIT auditTableAccess WITH ( STATE = OFF );

DROP SERVER AUDIT auditTableAccess
GO



/*
-- Inspect the audit output
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

SELECT *
INTO #tmp
FROM fn_get_audit_file ( 'S:\SQLAudit\auditTableAccess_*.sqlaudit', DEFAULT, DEFAULT );
GO


SELECT statement, MIN(event_time), MAX(event_time), COUNT(*) AS records
FROM #tmp
GROUP BY statement
GO
*/

-- Test 02 - SQL Audit END
------------------------------------------------------------------------------------------------




------------------------------------------------------------------------------------------------
-- Test 03 - Triggers START
-- Trial INSERT/UPDATE trigger with log table
------------------------------------------------------------------------------------------------
USE testAuditDb
GO

CREATE TABLE dbo.auditLog
    (
    auditLogLog     INT IDENTITY PRIMARY KEY,
    schemaName      SYSNAME NOT NULL,
    tableName       SYSNAME NOT NULL,
    dateAdded       DATETIME NOT NULL DEFAULT GETDATE(),
    addedBy         SYSNAME NOT NULL DEFAULT SUSER_NAME(),
    auditXML        XML
    )
GO


-- Generic audit trigger
CREATE TRIGGER trg_dbo__triggerTest ON auditSchema.auditTable
FOR INSERT, UPDATE, DELETE

AS

BEGIN

    IF @@rowcount = 0 RETURN

    SET NOCOUNT ON

    DECLARE @action VARCHAR(10)

    IF EXISTS ( SELECT * FROM inserted )
    AND EXISTS ( SELECT * FROM deleted )
        SET @action = 'UPDATE'
    ELSE IF EXISTS ( SELECT * FROM inserted )
        SET @action = 'INSERT'
    ELSE IF EXISTS ( SELECT * FROM deleted )
        SET @action = 'DELETE'

    INSERT INTO dbo.auditLog ( schemaName, tableName, auditXML )
    SELECT OBJECT_SCHEMA_NAME( parent_id ) schemaName, OBJECT_NAME( parent_id ) tableName,
        (
        SELECT
            @action "@action",
            ( SELECT 'inserted' source, * FROM inserted FOR XML RAW, TYPE ),
            ( SELECT 'deleted' source, * FROM deleted FOR XML RAW, TYPE )
        FOR XML PATH('mergeOutput'), TYPE
        ) x
    FROM sys.triggers
    WHERE OBJECT_ID = @@procid
      AND ( EXISTS ( SELECT * FROM inserted )
         OR EXISTS ( SELECT * FROM deleted )
          )

END
GO


-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2

SET @startTime = SYSDATETIME()

WHILE @i < 1000000
BEGIN

    INSERT INTO auditSchema.auditTable DEFAULT VALUES

    SET @i += 1
END

SET @endTime = SYSDATETIME()

SELECT DATEDIFF( second, @startTime, @endTime ) AS triggers
GO

-- Cleanup
TRUNCATE TABLE auditSchema.auditTable
DROP TABLE dbo.auditLog
DROP TRIGGER auditSchema.trg_dbo__triggerTest
GO

-- Test 03 - Triggers END
------------------------------------------------------------------------------------------------

Bien que l'option de déclenchement n'ait pas très bien fonctionné ici, mon code de déclenchement pourrait être simplifié en fonction de ce que vous souhaitez capturer et il vous permet d'accéder aux anciennes et nouvelles valeurs dans un format assez utilisable, ce que SQL Audit ne fait pas. J'ai utilisé cette technique pour une table de configuration à moindre activité et cela fonctionne assez bien. Selon ce que vous souhaitez capturer, vous pouvez également envisager de modifier la capture de données .

Dites-moi comment vous vous en sortez. Bonne chance.


3

Un des avantages de l'audit qui vient à l'esprit est qu'il enregistrera automatiquement qui l'allume et l'éteint, XE ne le fera pas tout de suite (bien que vous puissiez trouver un événement qui suit l'arrêt / le démarrage de XE). Vous pouvez également constater que les deux capturent des données différentes, selon exactement ce que vous voulez.

En ce qui concerne les tests, vous devez disposer d'une sauvegarde de base de données, capturer une trace de l'application en cours de chargement, puis restaurer la copie tout en effectuant une relecture / relecture avec audit / remplacement avec XE et en comparant les données de performances.

Quelles données de performance? C'est à vous. Pour certaines idées - Linchi Shea a fait une comparaison entre Audit et Trace en se concentrant sur les transactions / s, tandis que Kehayias a fait une comparaison entre Trace et XE en se concentrant sur les lots / s et le temps d'exécution global de la relecture.

Je vous encourage à lire les deux et leurs commentaires, car vous devez savoir que quoi que vous fassiez, ils seront sujets à interprétation. Il est difficile d'obtenir une pomme pour la comparaison des pommes. En outre, une trace / relecture peut ne pas simuler correctement la charge, par exemple lorsque votre application effectue de nombreux chargements en masse à partir de fichiers disque qui n'existent plus.

Mais l'important est que vous essayiez au moins une chose, afin que vous puissiez justifier vos décisions, et aussi bloguer à ce sujet pour le reste d'entre nous.


Lorsque vous dites que l'audit capture automatiquement les événements ON / OFF d'audit, parlez-vous des types d'actions AUSC dans la piste d'audit? De plus, pour des tests de performances idéales, je suppose que je devrais considérer les goulots d'étranglement actuels de l'application et voir si l'audit ou XE les aggravent.
karun_r
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.