Предотвратить параллельное выполнение с помощью блокировки таблицы (MySQL)

У меня есть таблица MySQL под названием cronjobs, в которой хранятся все необходимые cronjob (например, удаление старых писем, обновление возраста профиля и т. Д.). Для каждого cronjob есть определенный блок кода, который выполняется, если cronjob должен быть выполнен (я получил разные интервалы для разных cronjobs).

Для выполнения соответствующих cronjobs я получил PHP-скрипт, который выполняется crontab UNIX каждую минуту (вызывает execute_cronjobs_due.sh, который вызывает "php -f /path/to/file/execute_cronjobs_due.php").

При выполнении execute_cronjobs_due.php все cronjobs отмечаются, что они будут выполнены, так что другой вызов execute_cronjobs_due.php не вызовет параллельное выполнение того же самого cronjob, уже получая выполнение.

Теперь проблема: иногда выполнение занимает более 60 секунд, но программа crontab не вызывает execute_cronjobs_due.sh после этих 60 секунд. На самом деле происходит то, что execute_cronjobs_due.sh вызывается сразу после выполнения выполнения предыдущего crontab. И если выполнение занимает более 120 секунд, следующие два выполнения инициализируются одновременно.

График:

2015-06-15 10:00:00: выполнение execute_cronjobs_due.sh (занимает 140 секунд)

2015-06-15 10:02:20: два одновременных выполнения execute_cronjobs_due.sh

Поскольку он выполняется точно одновременно, нет смысла отмечать cronjob, что они выполняются, так как выборки (которые должны фактически исключать помеченный один раз) выполняются в одно и то же время. Так что обновление происходит сразу после того, как оба уже выбрали нужные cronjobs.

Как я могу решить эту проблему, чтобы не было одновременного выполнения cronjobs? Могу ли я использовать блокировки таблиц MySQL?

Большое спасибо за вашу помощь заранее,

Фредерик

2 ответа

Решение

Да, вы можете использовать блокировки таблиц mysql, но это может быть излишним для вашей ситуации. Во всяком случае, чтобы сделать это самым общим способом

  1. Убедитесь, что вы отключили автокоммит
  2. LOCK TABLES cronjobs;
  3. делай свои вещи
  4. РАЗБЛОКИРОВАТЬ СТОЛЫ

точный синтаксис и подробности читайте в документации, конечно, https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html, я лично никогда не использовал блокировку на уровне таблицы, так что, возможно, есть некоторые зацепки, я не в курсе

Что бы я сделал, если бы вы использовали движок таблиц InnoDB с оптимистической блокировкой:

  1. начать транзакцию в качестве первого шага в вашем скрипте
  2. получить идентификатор скрипта или что-то еще, может быть, процесс PID (getmypid()) или комбинация хост + пид. Или просто сгенерировать guid, если вы не знаете, что будет идеально
  3. сделать что-то вроде UPDATE cronjobs SET executed_by = my_id WHERE executed_by is null and /* whatever condition to get jobs to run */
  4. затем SELECT * FROM cronjobs where executed_by = my_pid
  5. делай свои вещи на что-нибудь выше, вернись
  6. UPDATE cronjobs set executed_by = null where executed_by = my_pid

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

При таком решении второй сценарий не даст сбой (технически), он просто запустит 0 заданий.

Минус в том, что вам придется убирать задания, которые были заявлены, но скрипт не смог пометить их как завершенные, но вам, вероятно, придется делать это в любом случае с текущим решением. Самый простой способ - это добавить столбец отметки времени, который будет отслеживать, когда заявка была запрошена в последний раз, и истечет через 15 минут или час, в зависимости от требований бизнеса (короткий псевдокод: первое обновление выполнит SET executed_by = my_id, started_at = NOW() where executed_by is null or (executed_by is not null and started_at < NOW() - 1 hour))

Как я могу решить эту проблему, чтобы не было одновременного выполнения cronjobs?

Есть несколько способов решить эту проблему. Они также могут быть полезны:

Мое предложение состоит в том, чтобы сделать это простым и использовать либо блокировку файла, либо проверку наличия файла.

Могу ли я использовать блокировки таблиц MySQL?

Да, но это немного излишне.

Вы должны использовать "таблицу обработки cronjob" со столбцом состояния cronjob ("ToDo, Started, Complete" или "Todo, Running, Done") и столбцом PID. Затем вы выбираете вакансии и отмечаете их состояние с помощью транзакций. Это гарантирует, что "Выбор задания из Todo" и "пометка его как запущенного / запущенного" выполняется за один шаг. В конце концов, у вас все еще может быть несколько exec вашего "сценария центральной обработки cronjob", но задания НЕ выбираются несколько раз для обработки.

Другие вопросы по тегам