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

Как предотвратить состояние состязания в базе данных MySQL, когда два соединения хотят обновить ту же запись?

Например, соединение 1 хочет увеличить счетчик "попыток". И второе соединение хочет сделать то же. Оба соединения SELECT количество "попыток", увеличьте значение и обоих UPDATE "попытки" с увеличенным значением. Внезапно "попытки" только "tries+1" вместо того, чтобы быть "tries+2", потому что оба соединения получили те же "попытки" и увеличили его одним.

Как решить эту проблему?

23
задан Lightness Races with Monica 14 August 2011 в 16:50
поделиться

2 ответа

Вот 3 разных подхода:

Атомарное обновление

update table set tries=tries+1 where condition=value;

, которое будет выполняться атомарно.

Использовать транзакции

Если вам действительно нужно сначала выбрать значение и обновить его в вашем приложении, вам, вероятно, придется использовать транзакции. Это означает, что вам придется использовать InnoDB, а не таблицы MyISAM. Ваш запрос будет примерно таким:

BEGIN; //or any method in the API you use that starts a transaction
select tries from table where condition=value for update;
.. do application logic to add to `tries`
update table set tries=newvalue where condition=value;
END;

если транзакция завершится неудачно, вам может потребоваться повторить ее вручную.

Схема версий

Распространенным подходом является введение столбца версии в вашу таблицу. Ваши запросы будут делать что-то вроде:

select tries,version from table where condition=value;
.. do application logic, and remember the old version value.
update table set tries=newvalue,version=version + 1 where condition=value and version=oldversion;

Если это обновление завершится неудачно / вернет 0 затронутых строк, это значит, что кто-то еще обновил таблицу за это время. Вы должны начать все сначала - то есть выбрать новые значения, выполнить логику приложения и повторить попытку обновления.

38
ответ дан 29 November 2019 в 01:32
поделиться

Используйте одну инструкцию вместо двух. Один оператор UPDATE , который выполняет и чтение, и запись, будет атомарным и не будет конфликтовать с другим одновременным обновлением.

UPDATE table SET tries = tries + 1 WHERE ...

Или вы можете использовать транзакции, чтобы сделать две операции атомарными.

BEGIN
SELECT ...
UPDATE ...
COMMIT

Или, что более примитивно, заблокируйте таблицу на время чтения / записи.

LOCK TABLES table WRITE
SELECT ...
UPDATE ...
UNLOCK TABLES
13
ответ дан 29 November 2019 в 01:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: