Как отслеживать изменения в базе данных

Ответ

Отслеживание изменений в БД — критически важная задача для аудита, синхронизации или реализации событийной архитектуры. Выбор подхода зависит от СУБД, фреймворка и требований.

1. Триггеры в БД

Создание триггеров AFTER INSERT/UPDATE/DELETE для записи изменений в таблицу-лог. Это надежный, но создающий нагрузку на БД метод.

CREATE TRIGGER TrackUserChanges
ON dbo.Users
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    INSERT INTO ChangeLog (TableName, ChangeType, ChangedData, ChangedAt)
    SELECT 'Users',
           CASE WHEN EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) THEN 'UPDATE'
                WHEN EXISTS(SELECT * FROM inserted) THEN 'INSERT'
                ELSE 'DELETE' END,
           (SELECT * FROM inserted FOR JSON PATH),
           GETDATE();
END;

2. Change Data Capture (CDC) или Temporal Tables

CDC (в SQL Server, PostgreSQL) отслеживает изменения на уровне транзакционного лога с минимальным влиянием на производительность.

-- SQL Server CDC
EXEC sys.sp_cdc_enable_db;
EXEC sys.sp_cdc_enable_table
    @source_schema = 'dbo',
    @source_name = 'Users',
    @role_name = NULL,
    @capture_instance = 'dbo_Users';
-- Запрос изменений
SELECT * FROM cdc.dbo_Users_CT;

Temporal Tables автоматически сохраняют историю версий строк.

3. Механизмы ORM (например, Entity Framework Core)

Change Tracker отслеживает изменения в контексте перед сохранением.

// Отслеживание измененных сущностей
var changedEntries = context.ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Modified)
    .ToList();

foreach (var entry in changedEntries)
{
    var originalValues = entry.OriginalValues;
    var currentValues = entry.CurrentValues;
    // Логирование изменений
}

Перехватчики (Interceptors) позволяют глобально обрабатывать команды перед выполнением.

4. Паттерн «Наблюдатель» (Observer) или Domain Events

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

public class EntityChangedEvent : INotification
{
    public string EntityType { get; set; }
    public string ChangeType { get; set; }
    public object EntityId { get; set; }
}

// В обработчике репозитория или сервиса
await _mediator.Publish(new EntityChangedEvent
{
    EntityType = nameof(User),
    ChangeType = "Updated",
    EntityId = user.Id
});

5. Внешние инструменты (Debezium)

Для сложных микросервисных архитектур используют Debezium — платформу для потоковой передачи изменений из БД (CDC) в Kafka, что позволяет другим сервисам реагировать на изменения.

Рекомендация: Для простых задач подойдут триггеры или Temporal Tables. Для .NET-приложений средней сложности — EF Core Change Tracker + Domain Events. Для распределенных систем — CDC через Debezium.

Ответ 18+ 🔞

Да ты посмотри, какие варианты-то есть! Прямо как в магазине — на любой вкус и цвет, от дешёвой селёдки до икры заморской. Сидишь такой, думаешь: "Ну как же мне за своей базой подглядывать, что там меняется?" А вариантов — овердохуища!

1. Триггеры в самой базе — классика жанра, но с подводными булыжниками. Это как поставить бабку-контролёршу на каждом углу подъезда. Всё видит, всё записывает в свою потрёпанную тетрадку. Надёжно? Ещё как. Но если жильцов много, бабка начнёт задыхаться, а подъезд — тормозить.

CREATE TRIGGER TrackUserChanges
ON dbo.Users
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    INSERT INTO ChangeLog (TableName, ChangeType, ChangedData, ChangedAt)
    SELECT 'Users',
           CASE WHEN EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) THEN 'UPDATE'
                WHEN EXISTS(SELECT * FROM inserted) THEN 'INSERT'
                ELSE 'DELETE' END,
           (SELECT * FROM inserted FOR JSON PATH),
           GETDATE();
END;

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

2. CDC или Волшебные Временные Таблицы — магия под капотом. Вот это уже ближе к хакингу, по-хорошему. CDC (Change Data Capture) — это как поставить прослушку прямо на транзакционный лог базы. База сама по себе работает, а где-то в сторонке тихонько пишется, кто, что и когда поменял. Красота!

-- Включаем CDC в SQL Server
EXEC sys.sp_cdc_enable_db;
EXEC sys.sp_cdc_enable_table
    @source_schema = 'dbo',
    @source_name = 'Users',
    @role_name = NULL,
    @capture_instance = 'dbo_Users';
-- А потом запрашиваем изменения как обычную таблицу
SELECT * FROM cdc.dbo_Users_CT;

А Temporal Tables — это вообще песня. Сказал базе: "Хочу историю!" и она тебе каждую версию строки сама хранит. Удалил запись? Она не удалилась, она просто переехала в историческую часть. Удобно, но не везде эту фичу завезли.

3. ORM-овские фокусы (Entity Framework Core). Тут уже работаем на уровне приложения. Change Tracker — это внутренний стукач EF. Он знает, что ты там поменял, пока не вызвал SaveChanges.

// Вылавливаем всё, что пошляпилось
var changedEntries = context.ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Modified)
    .ToList();

foreach (var entry in changedEntries)
{
    var originalValues = entry.OriginalValues; // Было
    var currentValues = entry.CurrentValues;   // Стало
    // И тут делаем с этим знанием что душе угодно
}

Плюс в том, что логика в коде приложения, и её можно тонко настраивать. Минус — если изменение пройдёт в обход твоего EF-контекста (прямой запрос, другая программа), то никто ничего не заметит. Доверия к такому механизму — ноль, если система не полностью под контролем.

4. События внутри приложения — архитектурный полёт мысли. Паттерн "Наблюдатель" или Domain Events. Сущность меняется и кричит на всю округу: "Эй, я обновилась!". А кто хочет — тот подписывается и слушает.

// Создаём событие
public class EntityChangedEvent : INotification
{
    public string EntityType { get; set; }
    public string ChangeType { get; set; }
    public object EntityId { get; set; }
}

// И где-то в сервисе шлём его в мир
await _mediator.Publish(new EntityChangedEvent
{
    EntityType = nameof(User),
    ChangeType = "Updated",
    EntityId = user.Id
});

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

5. Тяжёлая артиллерия — Debezium. Это когда твоя система уже разрослась до кучи микросервисов, и тебе нужно, чтобы изменение в одной базе моментально било набат по всем остальным. Debezium цепляется к логу базы (CDC) и транслирует все изменения в Apache Kafka. А оттуда их уже растаскивают кто куда хочет. Это мощно, масштабируемо, но и настраивать это — тот ещё геморрой. Для маленького проекта это как из пушки по воробьям.

Так что же выбрать, спросишь ты?

  • Просто и дёшево? Триггеры или Temporal Tables, если база позволяет. Быстро, но потом можешь ругаться на производительность.
  • Приложение на .NET и хочется контроля? EF Core Change Tracker + события. Логика у тебя в руках, но слепых зон полно.
  • Система распределённая, сервисов дохуя? Тогда только CDC, желательно через того же Debezium в Kafka. Геморроя — море, но других адекватных вариантов просто нет.

В общем, как всегда — нет идеального способа. Есть способ, который меньше всего порвёт твою конкретную жопу в твоей конкретной ситуации. Выбирай, блядь!