Одновременный запуск cron заданий, совокупность записей обработки

#php #mysql #cron

#php #mysql #cron

Вопрос:

Я борюсь с несколькими одновременно обедающими cronjobes с одной и той же проблемой процесса записи

Вступление

  • У меня есть таблица db, в которой присутствует более 100 клиентов
  • Для каждого клиента я должен запускать некоторый скрипт через cronjobe каждые 5 минут
  • Cronjobe немедленно извлекает каждого клиента из базы данных и обновляет последнюю использованную временную метку, чтобы следующий cron выбрал следующую обработанную запись на 5 минут старше
  • Обработка Cronjobe занимает около 30 секунд
  • Таким образом, это означает, что для обработки всех 100 клиентов в течение необходимых 5 минут мне потребуется около 10 симуляций cron jobs wokring
  • все cron определяются как: * * * * * php cron.php (таким образом, каждые 1 минуту съедается 10 кронджобов)
  • Cron выполняет некоторый сетевой поиск для этого IP-адреса клиента и регистрирует его в базе данных журналов. это должно происходить для каждого клиента в течение каждых 5 минут, так как позже я нарисую диаграмму на основе журналов для каждого клиента
  • код написан на PHP, БД — MySQL

Проблема в том, что когда одновременно запускается 10 cronjobes, случайным образом случается, что 2 cronjobes выбрали одного и того же клиента из базы данных для обработки

Таким образом, это означает, что 2 cron запускаются в одно и то же время (разница в микросекундах), оба одновременно выбирают последнюю необработанную строку (например, id: 17) из базы данных, затем обновляют тот же id17 в базе данных, 3-й обеденный cronjobe уже взял id: 18. Но мне нужно, чтобы каждый coronjobe брал уникальную следующую запись из базы данных, а не одну и ту же

As a workaround i tried to add random sleep(rand(1,10)) delay at beginning of cron.php but don't help mutch, random duplication still happens cause cron continuously selects last unprocessed next customer which sometime matches with another cornjobe next customer select at same time

Существуют ли какие-либо решения для этой ситуации???

Комментарии:

1. Вы можете назначить каждого клиента одному из десяти заданий cron на постоянной основе. Таким образом, клиент всегда обрабатывается одним и тем же заданием, и никаких коллизий возникнуть не может. Помимо такого базового решения: все это звучит очень странно. Вам действительно нужно тратить 30 секунд на обработку каждого клиента каждые 5 минут? Это заставляет меня задуматься, о чем это все. Для лучших решений было бы неплохо иметь идею, и мне тоже просто любопытно.

2. @KIKOSoftware спасибо за комментарий, на самом деле количество клиентов и cron будет постоянно увеличиваться, и было бы очень сложно оптимально назначить каждого клиента его персональному cornjobe. Cron выполняет некоторый сетевой поиск для этого IP-адреса клиента и регистрирует его в базе данных журналов. это должно происходить для каждого клиента в течение каждых 5 минут, так как позже я нарисую диаграмму на основе журналов — идея состоит в том, чтобы иметь много cronjobes, которые случайным образом выбирают последнего необработанного клиента и обрабатывают

3. Нетрудно назначить клиентов заданиям cron. Предположим, у вас есть 10 заданий, пронумерованных от 0 до 9, и 200 клиентов с уникальными идентификаторами от 0 до 199. Вы можете назначить клиентов следующим образом: $jobNo = $customerID % 10; . Аналогично, задание cron может просто выбрать каждого десятого клиента. Это не сложно сделать.

4. @KIKOSoftware ага, я вижу, спасибо, хороший подход, выбираемый по маске% id, но в этом случае, если администратор допустит, скажем, удаление всех клиентов, которые соответствуют cron с маской %5, тогда у этого 5-го cronjobe будет меньше работы, чем у oters (вместо этого он может помочь другим), и решение снова не будетоптимально, но да, мы избежим дублирования

5. Да, удаленные клиенты будут представлять небольшую проблему, но я думаю, что это можно преодолеть. Основная идея остается той же: сделайте так, чтобы клиент никогда не мог быть выбран несколькими заданиями cron.

Ответ №1:

Решение, которое вы ищете, — это совместная блокировка. Клиент должен быть помечен как «заблокированный» каким-либо заданием, и ни один скрипт не должен выбирать клиента, который заблокирован другим скриптом.

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

В MySQL вы можете сделать:

 $me = getmypid();
$conn->execute("SELECT GET_LOCK('choosing', 5) AS okay");
// Check the returned value of okay. If it is not 1, exit() immediately.
// choose some customer in some way. The smallest Id with OwnedByPid=0 for example. The query should be fast enough to run in under 5 seconds.
$conn->execute("UPDATE customers SET OwnedByPid={$me} WHERE id={$custId};");
$conn->execute("SELECT RELEASE_LOCK('choosing'");
//

// Do your work

$conn->execute("SELECT GET_LOCK('choosing', 5)");
$conn->execute("UPDATE customers SET OwnedByPid=0 WHERE OwnedByPid={$me};");
$conn->execute("SELECT RELEASE_LOCK('choosing')");
  

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

 $conn->execute("UPDATE customers SET OwnedByPid=0;");
  

Или вы можете добавить еще один столбец, OwningStart, установите для него значение NOW(), когда вы становитесь владельцем, чтобы вы могли проверять, когда OwningStart старше 30 секунд, и очищать его. Или пометьте его как свободный:

 SELECT MIN(Id) FROM customers WHERE OwnedByPid=0 OR OwningStart < NOW() - INTERVAL 2 MINUTE;
  

Комментарии:

1. Спасибо, похоже на решение, которое я ищу! Только один вопрос SELECT GET_LOCK(‘выбор’, 5) означает, что если другой cron запустит тот же SELECT GET_LOCK (‘выбор’, 5), он получит нулевой результат, и это означает, что мне нужно будет иметь какой-то бесконечный цикл в этом cron, который будет постоянно проверять «выбор» и после его освобождениясделайте свою собственную блокировку и сделайте запись правильно?

2. Нет, GET_LOCK блокируется (до запрошенных 5 секунд). За это время, которого достаточно для выполнения запроса для выбора идентификатора клиента, все остальные задания cron, выполняющие GET_LOCK, будут остановлены и ожидают. Таким образом, все они останутся в очереди. Если первый раздел выполняется за 0,01 секунды или меньше, что вполне вероятно, то 10 заданий будут ждать не более одной десятой секунды.