Аудит таблиц с помощью триггеров
Создавайте журнал аудита с помощью триггеров AFTER INSERT/UPDATE/DELETE, записывающих данные в таблицу audit_log
«Аудит таблиц с помощью триггеров» — бесплатный урок SQL Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.
Зачем нужен аудит?
Журналы аудита отвечают на вопросы «кто, что и когда изменил». Они необходимы для соответствия требованиям (HIPAA, SOX, расследований по праву GDPR на удаление данных) и операционной криминалистики.
Таблица аудита
Одна центральная таблица фиксирует каждое изменение:
CREATE TABLE audit_log (
id BIGSERIAL PRIMARY KEY,
ts TIMESTAMPTZ NOT NULL DEFAULT NOW(),
user_name TEXT NOT NULL DEFAULT CURRENT_USER,
table_name TEXT NOT NULL,
action TEXT NOT NULL, -- INSERT, UPDATE, DELETE
row_id TEXT,
old_data JSONB,
new_data JSONB
);Функция триггера аудита
Одна функция, которую можно повторно использовать для многих таблиц:
CREATE OR REPLACE FUNCTION audit_row()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO audit_log (table_name, action, row_id, old_data, new_data)
VALUES (
TG_TABLE_NAME,
TG_OP,
COALESCE(NEW.id::TEXT, OLD.id::TEXT),
CASE WHEN TG_OP IN ('UPDATE','DELETE') THEN to_jsonb(OLD) END,
CASE WHEN TG_OP IN ('INSERT','UPDATE') THEN to_jsonb(NEW) END
);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;Подключение к таблицам
Создайте триггер для AFTER INSERT/UPDATE/DELETE:
CREATE TRIGGER trg_audit_users
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_row();to_jsonb(NEW)
Приём to_jsonb позволяет обобщённо сохранить всю строку — без кода для каждого столбца. Он работает с любой таблицей, содержащей столбец id.
Отслеживание инициатора
Если приложение работает от имени одного пользователя БД, CURRENT_USER недостаточно. Передавайте пользователя приложения через GUC сеанса:
-- App sets:
SET LOCAL app.user_id = '42';
-- Trigger reads:
INSERT INTO audit_log (... actor_id ...) VALUES (..., current_setting('app.user_id', true)::BIGINT);Аудит определённых столбцов
Используйте условие WHEN триггера для выборочного аудита:
CREATE TRIGGER trg_audit_role_change
AFTER UPDATE OF role ON users
FOR EACH ROW WHEN (OLD.role IS DISTINCT FROM NEW.role)
EXECUTE FUNCTION audit_role_change();Таблицы аудита для отдельных таблиц
Альтернатива: одна таблица аудита для каждой реальной таблицы (например, users_audit) с той же схемой и метаданными аудита. Такие таблицы проще запрашивать, но для их сопровождения требуется больше DDL.
Аудит только изменений
Сохраняйте только изменённые столбцы:
INSERT INTO audit_log (table_name, action, changes)
VALUES (
TG_TABLE_NAME, TG_OP,
(SELECT jsonb_object_agg(key, value)
FROM jsonb_each(to_jsonb(NEW))
WHERE NEW.* IS DISTINCT FROM OLD.* AND to_jsonb(NEW)->key IS DISTINCT FROM to_jsonb(OLD)->key)
);Производительность таблицы аудита
В загруженных системах таблица аудита быстро растёт. Выполняйте секционирование по времени:
CREATE TABLE audit_log (...) PARTITION BY RANGE (ts);
CREATE TABLE audit_log_2024_q1 PARTITION OF audit_log
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');Компромисс производительности
Теперь каждая операция INSERT/UPDATE/DELETE записывает вторую строку. Для таблиц с очень высокой интенсивностью записи это может удвоить IO. Проверьте, отслеживайте и решите, оправдан ли такой компромисс.
Когда не следует использовать триггеры БД
Если Вам нужна интеграция с шиной событий, ставьте события в очередь с помощью NOTIFY или LISTEN либо используйте логическую репликацию или инструменты CDC (Debezium) вместо триггеров.
Итоги
Триггеры аудита — самый простой способ сохранять историю.
- Одна универсальная функция с to_jsonb(NEW/OLD)
- Подключайте её к каждой таблице, подлежащей аудиту
- Секционируйте таблицы аудита по времени
- Явно отслеживайте инициатора приложения через GUC
Быстрая проверка
Как внутри функции триггера аудита обобщённо сохранить новую строку в формате JSONB?
Часто задаваемые вопросы
Урок «Аудит таблиц с помощью триггеров» бесплатный?
Да — полный текст урока «Аудит таблиц с помощью триггеров» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.
Чему я научусь в уроке «Аудит таблиц с помощью триггеров»?
Создавайте журнал аудита с помощью триггеров AFTER INSERT/UPDATE/DELETE, записывающих данные в таблицу audit_log Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Academy?
Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Аудит таблиц с помощью триггеров»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Academy?
Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Устройство триггеров: BEFORE/AFTER, FOR EACH ROW
- Основы функций PL/pgSQL
- Блоки DO и анонимный код
- Аудит таблиц с помощью триггеров