Запланированные события (Events)

В реальных приложениях часто возникает необходимость выполнять определённые действия автоматически и по расписанию: очищать старые записи, обновлять статистику, формировать отчёты. Для таких задач SQL предоставляет механизм запланированных событий.

Событие — это задача, которую база данных запускает сама по расписанию. Вы настраиваете — оно работает автоматически.

События в MySQL похожи на планировщик задач в операционной системе: вы создаёте задачу один раз, а база данных выполняет её автоматически по расписанию.

В PostgreSQL для автоматического выполнения задач используется расширение pg_cron. Это расширение позволяет планировать SQL-команды с использованием синтаксиса cron (как в Unix-системах).

Когда он пригодится

Запланированные события помогают автоматизировать следующие задачи:

  • Очистка данных: удаление устаревших записей логов или временных данных
  • Обновление статистики: пересчёт агрегированных данных для аналитики
  • Генерация отчётов: автоматическое формирование периодических отчётов
  • Резервное копирование: создание копий важных данных

Как включить планировщик

Прежде чем создавать события, необходимо убедиться, что планировщик событий включён:

MySQL 8.1
SHOW VARIABLES LIKE 'event_scheduler';

Если планировщик выключен, включите его:

MySQL 8.1
SET GLOBAL event_scheduler = ON;

Для использования запланированных задач в PostgreSQL нужно установить расширение pg_cron:

MySQL 8.1
CREATE EXTENSION IF NOT EXISTS pg_cron;

Важно: Расширение pg_cron может требовать прав суперпользователя и дополнительной настройки PostgreSQL. В облачных сервисах (AWS RDS, Azure) оно может быть уже предустановлено.

Создание одноразового события

Начнём с самого простого — события, которое выполнится один раз в определённое время:

MySQL 8.1
CREATE EVENT cleanup_old_logs
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
DO
    DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY;

Это событие удалит записи логов старше 30 дней через 24 часа после создания события.

Разберём синтаксис:

  • CREATE EVENT cleanup_old_logs — создаём событие с именем cleanup_old_logs
  • ON SCHEDULE AT — указываем, когда событие должно выполниться
  • CURRENT_TIMESTAMP + INTERVAL 1 DAY — время выполнения (через 1 день)
  • DO — код, который нужно выполнить (любой SQL-оператор)
MySQL 8.1
SELECT cron.schedule(
    'cleanup_old_logs',
    '0 3 * * *',
    'DELETE FROM logs WHERE created_at < NOW() - INTERVAL ''30 days'''
);

Это событие будет выполняться каждый день в 3:00 утра и удалять записи логов старше 30 дней.

Разберём синтаксис:

  • cron.schedule() — функция для создания запланированной задачи
  • 'cleanup_old_logs' — имя задачи
  • '0 3 * * *' — расписание в формате cron (минута час день месяц день_недели)
  • Последний параметр — SQL-команда для выполнения

Формат cron расписания:

Формат cron расписания

Создание повторяющегося события

Чаще всего события нужно запускать периодически — каждый день, час или минуту:

MySQL 8.1
CREATE EVENT update_statistics
ON SCHEDULE EVERY 1 HOUR
DO
BEGIN
    UPDATE product_stats SET
        total_sales = (SELECT SUM(amount) FROM orders WHERE product_id = product_stats.product_id),
        last_updated = NOW();
END;

Это событие будет обновлять статистику продаж каждый час.

Разберём синтаксис:

  • ON SCHEDULE EVERY 1 HOUR — выполнять каждый час
  • BEGIN ... END — блок из нескольких SQL-операторов

Варианты интервалов:

  • EVERY 1 MINUTE — каждую минуту
  • EVERY 1 HOUR — каждый час
  • EVERY 1 DAY — каждый день
  • EVERY 1 WEEK — каждую неделю
  • EVERY 1 MONTH — каждый месяц
  • EVERY 30 SECOND — каждые 30 секунд
MySQL 8.1
SELECT cron.schedule(
    'update_statistics_hourly',
    '0 * * * *',
    $$
    UPDATE product_stats SET
        total_sales = (SELECT SUM(amount) FROM orders WHERE product_id = product_stats.product_id),
        last_updated = NOW()
    $$
);

Это событие будет обновлять статистику продаж каждый час (в начале каждого часа).

Примеры расписаний:

  • '*/5 * * * *' — каждые 5 минут
  • '0 * * * *' — каждый час (в начале часа)
  • '0 0 * * *' — каждый день в полночь
  • '0 0 * * 0' — каждое воскресенье в полночь
  • '0 9 1 * *' — первого числа каждого месяца в 9:00

Событие с ограниченным сроком действия

Иногда нужно, чтобы событие работало только в определённый период:

MySQL 8.1
CREATE EVENT seasonal_discount
ON SCHEDULE EVERY 1 DAY
STARTS '2025-12-01 00:00:00'
ENDS '2025-12-31 23:59:59'
DO
    UPDATE products SET price = price * 0.9 WHERE category = 'seasonal';

Это событие будет применять скидку 10% на сезонные товары каждый день в течение декабря 2025 года.

Новые элементы:

  • STARTS — начало периода действия события
  • ENDS — конец периода действия события

После указанной даты событие автоматически прекратит выполняться.

В pg_cron нет встроенной поддержки автоматического завершения задач, но можно включить проверку даты в саму команду:

MySQL 8.1
SELECT cron.schedule(
    'seasonal_discount',
    '0 0 * * *',
    $$
    UPDATE products
    SET price = price * 0.9
    WHERE category = 'seasonal'
      AND CURRENT_DATE BETWEEN '2025-12-01' AND '2025-12-31'
    $$
);

Альтернативно, можно создать задачу на удаление события в конце периода:

MySQL 8.1
SELECT cron.schedule(
    'remove_seasonal_discount',
    '0 0 1 1 *',  -- 1 января в полночь
    $$SELECT cron.unschedule('seasonal_discount')$$
);

Просмотр существующих событий

Чтобы увидеть все созданные события:

MySQL 8.1
SHOW EVENTS;

Для просмотра событий в конкретной базе данных:

MySQL 8.1
SHOW EVENTS FROM your_database_name;

Чтобы увидеть все запланированные задачи:

MySQL 8.1
SELECT * FROM cron.job;

Это вернёт таблицу со всеми задачами, включая их расписание и команды.

Для просмотра истории выполнения задач:

MySQL 8.1
SELECT * FROM cron.job_run_details
ORDER BY start_time DESC
LIMIT 10;

Управление событиями

Временное отключение события:

MySQL 8.1
ALTER EVENT cleanup_old_logs DISABLE;

Включение события:

MySQL 8.1
ALTER EVENT cleanup_old_logs ENABLE;

Изменение расписания события:

MySQL 8.1
ALTER EVENT cleanup_old_logs
ON SCHEDULE EVERY 2 HOUR;

Удаление события:

MySQL 8.1
DROP EVENT IF EXISTS cleanup_old_logs;

Удаление запланированной задачи:

MySQL 8.1
SELECT cron.unschedule('cleanup_old_logs');

Или по ID задачи:

MySQL 8.1
SELECT cron.unschedule(42);  -- где 42 - это jobid из таблицы cron.job

Изменение задачи:

В pg_cron нельзя изменить существующую задачу напрямую. Нужно удалить старую и создать новую:

MySQL 8.1
-- Удаляем старую
SELECT cron.unschedule('cleanup_old_logs');

-- Создаём новую с обновлённым расписанием
SELECT cron.schedule(
    'cleanup_old_logs',
    '0 */2 * * *',  -- Каждые 2 часа
    'DELETE FROM logs WHERE created_at < NOW() - INTERVAL ''30 days'''
);

Важные моменты при работе с событиями

  1. Права доступа: Для создания событий нужна привилегия EVENT.

  2. Временная зона: События выполняются по временной зоне сервера базы данных.

  3. Производительность: Избегайте создания событий со слишком коротким интервалом (каждую минуту), это может повлиять на производительность.

  1. Права доступа: Для использования pg_cron обычно требуются права суперпользователя или специальная настройка.

  2. Временная зона: Задачи pg_cron выполняются по временной зоне PostgreSQL (можно проверить через SHOW timezone;).

  3. Производительность: Pg_cron проверяет расписание каждую секунду, поэтому минимальная точность — 1 минута.

  4. Логирование: Все выполнения задач сохраняются в таблице cron.job_run_details, что полезно для отладки.

Самопроверка

Какой минимальный интервал можно использовать для повторяющихся событий?

Запланированные события — это мощный инструмент для автоматизации рутинных задач в базе данных. Они помогают поддерживать чистоту данных, обновлять статистику и выполнять регламентные операции без участия разработчиков! 🚀