2013-03-08 2 views
1

Я понимаю, что Apache создает отдельный процесс PHP для каждого входящего запроса. Это означает, что если у меня есть код, который делает что-то вроде:Условия гонки с Apache, MySQL и PHP

  1. проверки, если запись существует
  2. , если запись не существует, создайте его

Тогда это восприимчивое к состоянию гонки, является не это? Если одновременно поступают два запроса, и оба они одновременно ударяют (1), они оба возвращаются в ложь, а затем обе пытаются вставить новую запись.

Если да, то как люди справляются с этим? Будет ли создание транзакции MySQL вокруг этих 2 запросов решить проблему, или нам нужно сделать полную блокировку таблицы?

+0

INSERT IGNORE, LOCK TABLE или используя индекс UNIQUE в столбце, который вы не хотите дублировать, являются хорошими кандидатами. –

+0

См. Также http://stackoverflow.com/questions/1361340/how-to-insert-if-not-exists-in-mysql –

+0

'SELECT ...FOR UPDATE' –

ответ

2

Насколько я знаю, вы не можете создать транзакцию через разные соединения. Возможно, одним из решений было бы установить столбец, который вы проверяете, чтобы быть уникальным. Таким образом, если два соединения сделаны на 10, а 10 не существует. Они оба попытаются создать 10. Сначала будет завершена вставка строки, и все будет хорошо; то соединение только второй позади провалится, потому что столбец не уникален. Если вы поймаете исключение, которое будет выброшено, вы можете впоследствии записать SELECT из базы данных.

0

Я считаю, что обработка ошибок на втором этапе должна быть достаточной. Если два процесса пытаются создать запись, то один из них будет терпеть неудачу, если вы настроили таблицу MySQL соответствующим образом. Использование UNIQUE в правильных полях - один из способов сделать трюк.

0

Apache не создает отдельный процесс PHP для каждого входящего запроса. В нем используется пул процессов (по умолчанию, режим предкарта) или потоки.

Условия гонки, как вы упомянули, также могут быть связаны с (или причиной) DB «Deadlock». @see what is deadlock in a database?

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

Также обратите внимание, что MySQL не поддерживает вложенные транзакции: Вы не можете иметь транзакции внутри транзакций, так как первая фиксация будет передавать все.

+0

+1 для разметки нитей – landons

+3

это простое условие гонки и не имеет ничего общего с тупиками –

+3

с использованием транзакций (в одиночку) не решит проблему, так как две транзакции могли видеть, что запись еще не существует. –

1

Честно говоря, я очень редко сталкиваюсь с этой ситуацией. Часто его можно смягчить путем переоценки бизнес-требований. Даже если два разных пользователя пытались вставить точные данные, я бы отложил управление дубликатами пользователей, а не приложение.

Однако, если бы возникла причина принудительного применения уникального ограничения в логике приложения, я бы использовал запрос INSERT IGNORE... ON DUPLICATE KEY UPDATE... (с соответствующим индексом UNIQUE в таблице, конечно).

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