2014-02-20 4 views
0

У меня есть следующий алгоритм:MySQL: одновременный доступ

  1. Выберите строку, которая не имеет никакого ответа и не блокируется (состояние 0)
  2. Заблокировать эту строку, чтобы запретить другим пользователям получать его (статус 1)

есть статус 2-й ряд имеет ответ и завершен.

SELECT * FROM details WHERE RowStatus=0 AND taskid=".$task_id." ORDER BY RAND() LIMIT 1 

UPDATE details SET RowStatus=1 ,Agent='".$_SESSION['username']."' where TaskID=".$row['TaskID']." and RowId=".$row['RowId'] 

Проблема заключается в том, что если у меня есть 10 строк слева и 8 агентов, работающих на задачу, иногда бывает, что они представляют предыдущий ряд точно то же самое время. Затем оба они выбирают одну и ту же строку, и только одна из них блокирует строку с его именем. Значение - один из них работает над той же строкой.

Его звучит как второстепенная проблема, но почему-то это произошло 5-10 раз в задаче на 2500 строк. Есть ли способ заблокировать строку в то же время, что и ее выбор? Запретить другому пользователю выбирать эту строку?

+0

Есть ли реальная причина, по заказу рандов() или это просто, чтобы попытаться избежать конфликтов? –

+0

Вы можете использовать транзакции, если вы работаете над 'InnoDB' –

+0

Сначала я не использовал случайные, но тогда вероятность того, что они оба получат одинаковые строки, увеличится. – Alex

ответ

0

вы должны блокировать строку (конечно, если вы используете InnoDB). Сценарий ниже:

begin; 
select ... FOR UPDATE; 

doing what you want with task 

update selected task 

commit; 
0

Один из способов (гораздо лучше) используют транзакцию: PHP + MySQL transactions examples

Вторых варианты вносят UPDATE первый (блокировка задачи), а затем выберите задачу Агентом и статусом.

+0

Как я могу заблокировать строку, если я не знаю, что такое выбранная строка? – Alex

0

Простой способ может быть:

После UPDATE, запустить SELECT на этой строке и убедитесь, что Agent является текущий пользователь. Если это не так, повторите выбор новой строки

+0

Интересное решение, спасибо – Alex

0

У меня есть идея, как это сделать без специального доступа, хранимых процедур, триггеров или транзакций.

Вам необходимо создать таблицу, названную, например, Semaphores с двумя колонками - name (text) и state (int). И для вашей цели мы создадим одну строку под названием details с состоянием 0.

Тогда ваш код может выглядеть следующим образом:

$semaphore = query("UPDATE Semaphore SET state = 1 WHERE name = 'details'"); // It's just pseudo code for querying database - do it yourself 

while(affected_rows($semaphore) == 0) { 
    $semaphore = query("UPDATE Semaphore SET state = 1 WHERE name = 'details'"); 
    sleep(10); // sleep to not query too often 
} 

fetch_and_query("SELECT * FROM details WHERE RowStatus=0 AND taskid=".$task_id." ORDER BY RAND() LIMIT 1"); 

query("UPDATE details SET RowStatus=1 ,Agent='".$_SESSION['username']."' where TaskID=".$row['TaskID']." and RowId=".$row['RowId']); 

query("UPDATE Semaphore SET state = 0"); 

Таким образом, каждый пользователь, который выполняет этот запрос будет ждать других чтобы закончить их команду SELECT + UPDATE. Также это всего лишь трюк - работа блокировки делает часть с обновлением таблицы Semaphore, вам просто нужен один запрос, чтобы узнать, было ли значение блокировки равным 0. Если бы это было тогда, то функция affected_rows сообщит вам, что вы может продолжаться с вашим скриптом (обратите внимание, что affected_rows может быть в PHP, например, mysqli_affected_rows, а также любой другой эквивалент). Помните, что при обновлении строки, которая не нуждается в обновлении, эта функция все равно вернет значение нуля.

Мой ответ на моем блоге: http://kelostrada.pl/en/mysql-en/locking-rows-with-myisam-php/

Смежные вопросы