У меня есть сценарий, который выбирает строку из базы данных MySQL. Затем обновляет эту строку. Как это:
$statement = $db->prepare("SELECT id, link from persons WHERE processing = 0"); $statement->execute(); $row = $statement->fetch(); $statement = $db->prepare("UPDATE persons SET processing = 1 WHERE id = :id"); $success = $statement->execute(array(':id' => $row['id']));
Сценарий вызывает этот PHP-код несколько раз одновременно. И иногда он выбирает строку, хотя она должна быть «processing = 1», потому что другой скрипт вызывает ее в точное время.
Как я могу избежать этого?
Что вам нужно сделать, так это добавить какой-то замок, чтобы предотвратить условия гонки, подобные тем, которые вы создали:
UPDATE persons SET processing=1 WHERE id=:id AND processing=0
Это позволит избежать двойной блокировки.
Чтобы это еще больше улучшить, создайте столбец блокировки, который вы можете использовать для утверждения:
UPDATE persons SET processing=:processing_uuid WHERE processing IS NULL LIMIT 1
Для этого требуется VARCHAR
, индексированный столбец processing
используемый для утверждения, который имеет значение по умолчанию NULL
. Если вы получите строку, измененную в результатах, вы заявили запись и можете пойти и работать с ней, используя:
SELECT * FROM persons WHERE processing=:processing_uuid
Каждый раз, когда вы пытаетесь потребовать, создайте новый ключ UUID претензии.
Попробуйте использовать транзакции для своих запросов. Читайте о них на сайте mysql dev
Вы можете обернуть свой код:
$dbh->beginTransaction(); // ... your transactions here $dbh->commit();
Здесь вы найдете документацию.
Использовать SELECT FOR UPDATE
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
например
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
Оберните это в транзакцию, и блокировки будут освобождены при завершении транзакции.