2015-06-15 5 views
1

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

Для выполнения должных cronjobs я получил скрипт PHP, который выполняется каждую минуту с помощью UNIX crontab (вызывает 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?

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

Фредерик

+0

Какой cron вы используете, который не выполняет задания по заданному расписанию, но вместо этого ожидает завершения других заданий? – Eborbob

ответ

2

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

  1. Убедитесь, что вы Autocommit от
  2. LOCK TABLES cronjobs;
  3. сделать ваши вещи
  4. UNLOCK TABLES

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

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

  1. старта сделки как первое, что в сценарии
  2. получить идентификатор сценария или любой другой, может быть process pid (getmypid()) или комбинация host + pid.Или просто сгенерировать 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 заданий.

Minus - это то, что вам нужно будет очистить задание, которое было заявлено, но скрипт не смог пометить их как готовые, но вам, вероятно, придется это делать в любом случае с текущим решением. Проще всего было бы добавить столбец временной метки, которая будет отслеживать, когда была работой заявленной в последний раз и истекает срок его после т.е. 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))

0

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

Существует несколько способов решения этой проблемы. Они могут быть полезны как хорошо:

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

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

Да, но это немного перебор.

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