Расширенные события против аудита SQL - влияние на производительность

8

Я хотел бы настроить в своей базе данных систему контрольных журналов, чтобы отслеживать UPDATE/INSERTзаявления в конкретной таблице с очень высокой активностью. Передо мной два варианта: использовать встроенную систему аудита SQL Server или использовать расширенные события.

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

Можно ли как-нибудь проверить, какая система больше влияет на сервер? Если бы я мог знать, что на самом деле происходит при создании любого сеанса XE, это помогло бы мне проанализировать влияние на сервер.

Мы рассмотрели триггеры и пропустили этот вариант из-за накладных расходов. Но это было решено только на основе информации из Интернета.

karun_r
источник
Не все события, захваченные с помощью аудита SQL, доступны через XEvents. SQL Audit использует тот же механизм, что и XEvents, но они являются отдельными функциями.
Да, я узнал об этом. Но когда мы провели какое-то нагрузочное тестирование (см. Ниже), мы заметили, что XE имеет больше накладных расходов, чем Audit. Если аудит использует XE в фоновом режиме, есть идеи, почему он вызывает дополнительные издержки?
karun_r

Ответы:

3

Я создал простую тестовую установку для проверки SQL Server Audit на наличие триггеров и, возможно, других вариантов. В моих тестах по вставке 1 миллиона строк в таблицу я получил 52, 67 и 159 секунд для базовой линии, аудита SQL и моего триггера соответственно:

Результаты теста

Теперь это не особенно научно, но потенциально предлагает вам один из способов сравнения подходов. Посмотрите скрипт и посмотрите, может ли он быть вам полезен:

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
------------------------------------------------------------------------------------------------

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

Дайте мне знать, как вы справляетесь со своими испытаниями. Удачи.

wBob
источник
3

Одно из преимуществ аудита, которое приходит на ум, заключается в том, что он будет автоматически записывать, кто его включает и выключает, XE не будет делать это «из коробки» (хотя вы можете найти событие, которое отслеживает остановку / запуск XE). Вы также можете обнаружить, что они собирают разные данные, в зависимости от того, что именно вы хотите.

Что касается проведения некоторого тестирования, вам необходимо иметь резервную копию базы данных, захватить трассировку приложения под нагрузкой, а затем восстановить копию, выполняя воспроизведение / воспроизведение с проверкой / заменой на XE и сравнивая данные о производительности.

Какие данные о производительности? Тебе решать. Для некоторых идей - Линчи Ши сделал сравнение между Audit и Trace, сосредоточившись на транзакциях в секунду, в то время как Kehayias провел сравнение между Trace и XE, сосредоточившись на пакетах в секунду и общем времени выполнения воспроизведения.

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

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

Коди Кониор
источник
Когда вы говорите, что аудит будет автоматически захватывать события включения / выключения аудита, вы говорите о типах действий AUSC в журнале аудита? Кроме того, для идеального тестирования производительности, я думаю, я должен рассмотреть текущие узкие места приложения и посмотреть, не ухудшит ли их аудит или XE.
karun_r