Планировщик событий
В реальных приложениях часто возникает необходимость выполнять определённые действия автоматически и по расписанию: очищать старые записи, обновлять статистику, формировать отчёты.
Для таких задач MySQL предоставляет механизм запланированных событий.
События в MySQL похожи на планировщик задач в операционной системе: вы создаёте задачу один раз, а база данных выполняет её автоматически по расписанию.
Для таких задач в PostgreSQL используют расширение pg_cron. Оно позволяет создавать запланированные задачи с помощью синтаксиса cron (как в Unix-системах).
Когда он пригодится
Запланированные события помогают автоматизировать следующие задачи:
С помощью pg_cron можно автоматизировать:
- Очистку данных: удаление устаревших записей логов или временных данных
- Обновление статистики: пересчёт агрегированных данных для аналитики
- Генерацию отчётов: автоматическое формирование периодических отчётов
- Обслуживание данных: перенос записей в архивные таблицы и другие регламентные операции
Как включить планировщик
Прежде чем создавать события, необходимо убедиться, что планировщик событий включён:
MySQL 8.1SHOW VARIABLES LIKE 'event_scheduler';
Если планировщик выключен, включите его:
MySQL 8.1SET GLOBAL event_scheduler = ON;
Эта команда меняет настройку до следующего перезапуска сервера и требует права на изменение глобальных системных переменных. Для постоянного включения планировщика используют конфигурацию сервера или SET PERSIST, если команда доступна в используемой версии MySQL.
Сначала pg_cron нужно установить на сервер, добавить в shared_preload_libraries и перезапустить PostgreSQL. После этого расширение создают в базе данных:
MySQL 8.1CREATE EXTENSION IF NOT EXISTS pg_cron;
Одноразовый запуск
Начнём с самого простого — события, которое выполнится один раз в определённое время:
Начнём с самого простого — задачи, которая выполнится один раз в определённое время:
MySQL 8.1CREATE 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-оператор или составной оператор BEGIN ... END
В pg_cron нет отдельного типа расписания для однократного запуска. Для такой задачи используют внешний планировщик приложения или создают временную задачу и удаляют её после выполнения.
Например, эта задача при ближайшем наступлении 3:00 удалит старые записи логов, а затем удалит саму себя:
MySQL 8.1SELECT cron.schedule( 'cleanup_old_logs_once', '0 3 * * *', $command$ DO $$ BEGIN DELETE FROM logs WHERE created_at < NOW() - INTERVAL '30 days'; PERFORM cron.unschedule('cleanup_old_logs_once'); END; $$; $command$ );
Обычно расписание '0 3 * * *' запускает задачу каждый день в 3:00. Но после первого выполнения задача вызывает cron.unschedule() для собственного имени, поэтому повторного запуска не будет.
Периодический запуск
Чаще всего события нужно запускать периодически — каждый день, час или минуту:
Чаще всего задачи нужно запускать периодически — каждый день, час или минуту:
MySQL 8.1CREATE 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.1SELECT 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-расписания:

MySQL 8.1SELECT 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
- '30 seconds' — каждые 30 секунд в pg_cron 1.5 и новее
Интервал в секундах задаётся отдельной строкой, а не шестым полем cron-выражения. pg_cron поддерживает значения от 1 до 59 секунд.
Ограничение периода выполнения
Иногда нужно, чтобы событие работало только в течение определённого периода.
Иногда нужно, чтобы задача работала только в течение определённого периода.
MySQL 8.1CREATE EVENT temporary_log_cleanup ON SCHEDULE EVERY 1 DAY STARTS CURRENT_TIMESTAMP ENDS CURRENT_TIMESTAMP + INTERVAL 30 DAY DO DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY;
Это событие будет удалять устаревшие логи раз в день в течение 30 дней.
Новые элементы:
- STARTS — начало периода действия события
- ENDS — конец периода действия события
После ENDS событие прекратит выполняться и по умолчанию будет удалено. Чтобы сохранить его определение, при создании нужно добавить ON COMPLETION PRESERVE.
В расписании pg_cron нельзя указать дату автоматического завершения задачи. Например, задачу для ежедневной очистки логов в течение ближайших 30 дней создают с обычным расписанием:
MySQL 8.1SELECT cron.schedule( 'temporary_log_cleanup', '0 0 * * *', $$DELETE FROM logs WHERE created_at < NOW() - INTERVAL '30 days'$$ );
Чтобы задача остановилась автоматически, используют тот же приём, что и в примере одноразового запуска выше: создают задачу stop_temporary_log_cleanup на дату через 30 дней. Тело команды для неё выглядит так:
MySQL 8.1DO $$ BEGIN PERFORM cron.unschedule('temporary_log_cleanup'); PERFORM cron.unschedule('stop_temporary_log_cleanup'); END; $$;
Первый вызов удаляет задачу очистки, а второй — служебную задачу. В расписании служебной задачи указывают минуту, час, день и месяц даты, которая наступит через 30 дней.
Просмотр расписания
Чтобы увидеть все созданные события:
MySQL 8.1SHOW EVENTS;
Для просмотра событий в конкретной базе данных:
MySQL 8.1SHOW EVENTS FROM your_database_name;
Чтобы увидеть доступные текущему пользователю запланированные задачи:
MySQL 8.1SELECT * FROM cron.job;
Обычный пользователь видит только собственные задачи. Суперпользователь и роль с атрибутом BYPASSRLS могут видеть задачи других пользователей.
Для просмотра истории выполнения задач:
MySQL 8.1SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10;
Управление расписанием
Временное отключение события:
MySQL 8.1ALTER EVENT cleanup_old_logs DISABLE;
Включение события:
MySQL 8.1ALTER EVENT cleanup_old_logs ENABLE;
Изменение расписания события:
MySQL 8.1ALTER EVENT cleanup_old_logs ON SCHEDULE EVERY 2 HOUR;
Удаление события:
MySQL 8.1DROP EVENT IF EXISTS cleanup_old_logs;
Удаление запланированной задачи:
MySQL 8.1SELECT cron.unschedule('cleanup_old_logs');
Или по ID задачи:
MySQL 8.1SELECT cron.unschedule(42); -- где 42 - это jobid из таблицы cron.job
Изменение задачи:
Расписание, команду и активность существующей задачи можно изменить с помощью cron.alter_job():
MySQL 8.1SELECT cron.alter_job( 42, schedule := '0 */2 * * *' );
Здесь 42 — jobid задачи из таблицы cron.job.
-
Права доступа: Для создания событий нужна привилегия EVENT.
-
Временная зона: Расписание интерпретируется в значении time_zone текущей сессии при создании или изменении события. MySQL сохраняет эту временную зону вместе с событием.
-
Параллельные запуски: Если событие выполняется дольше своего интервала, MySQL может запустить несколько его экземпляров одновременно. При необходимости это нужно предотвращать блокировкой или другой защитой от параллельного выполнения.
-
Производительность: Допустимая частота зависит от стоимости операции и нагрузки на базу данных, а не от универсального минимального интервала.
-
Права доступа: Суперпользователь обычно устанавливает расширение, после чего обычным пользователям можно выдать USAGE на схему cron. Задача выполняется с правами создавшего её пользователя.
-
Временная зона: Cron-выражения используют настройку cron.timezone, которая по умолчанию равна GMT. Текущее значение можно проверить командой SHOW cron.timezone;.
-
Интервалы: pg_cron 1.5 и новее поддерживает интервалы от 1 до 59 секунд. Частоту нужно выбирать с учётом стоимости задачи и нагрузки.
-
Параллельные запуски: pg_cron не запускает одновременно несколько экземпляров одной задачи. Если наступил следующий срок, пока задача ещё работает, новый запуск будет поставлен в очередь.
-
Логирование: При включённой настройке cron.log_run сведения о запусках сохраняются в таблице cron.job_run_details. По умолчанию логирование включено.
Самопроверка
Какой минимальный интервал можно использовать для повторяющихся событий?
Какой минимальный интервал можно использовать для повторяющихся задач?
Запланированные события — это мощный инструмент для автоматизации рутинных задач в базе данных. Они помогают поддерживать чистоту данных, обновлять статистику и выполнять регламентные операции без участия разработчиков! 🚀
Запланированные задачи — это мощный инструмент для автоматизации рутинных задач в базе данных. Они помогают поддерживать чистоту данных, обновлять статистику и выполнять регламентные операции без участия разработчиков! 🚀