Member 11040029 Ответов: 1

Предотвратить повторяющиеся вставки и тупик читает


Сейчас я занимаюсь разработкой многопользовательского приложения с MySQL бэкэнда.

Один из моих столов:
create table accountInfo
(
  id smallint primary key, 
  name varchar(30) not null
)Engine = InnoDB charset = utf8;


Значение столбца id начинается с 1 и должно увеличиваться для каждого нового запроса вставки.
Я также не должен использовать столбец auto_increment для поля id.

Теперь рассмотрим ситуацию,
Есть 2 пользователя A и B, которые, скорее всего, будут вставлять записи одновременно в один и тот же момент времени. Для этого они сначала считывают max (id) из таблицы и увеличивают его на 1, а затем выполняют запрос insert с увеличенным значением поля ID.
Но, поскольку оба читают значение ID одновременно, оба получат одно и то же значение, скажем 1. Теперь при вставке один из них получит ошибку повторяющегося значения, что повлияет на пользовательский опыт.

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

Я попытался реализовать этот сценарий с помощью FOR UPDATE lock. Но происходит то, что любой из пользователей A или B оказывается в тупике.
Я не понимаю, почему возникает этот тупик.

Что я уже пробовал:

Пользователь а выполняет:
1. выберите Max(ID) как код от счета в качестве ID для обновления;
2. получает id = 1;
3. _ledgerID = 1;

Пользователь B выполняет (одновременно):
1. выберите Max(ID) как код от счета в качестве ID для обновления;
2. пользователь 2 ждет....

Пользователь А
1. Вставьте в account_ledger(id,name) значения("+_ledgerID+",'Jack');
2. по команде.ExecuteNonQuery (), исключение происходит как "тупик найден при попытке получить блокировку.."

Пользователь B
1. получает id = 1

1 Ответов

Рейтинг:
2

#realJSOP

В запросе попробуйте обновить и оценить количество измененных записей. Если это < 1, то сделайте вставку.

Я думаю, что секрет в том, чтобы правильно обрабатывать исключения. Поместите свой вызов ExecuteNonQuery в цикл while и выйдите из этого цикла, когда нет никаких исключений. Не забудьте включить короткую задержку (500 мс или около того) перед повторной попыткой запроса.

РЕДАКТИРОВАТЬ ===========================

Это то, что я делаю в MS-SQL, и MYSQL должен быть похожим:

UPDATE [table]
SET [field1]=@value1
    ...
WHERE [field2] = @value2
IF (@@ROWCOUNT = 0)
    INSERT...


Конечно, вам, вероятно, нужно поместить свои выписки о блокировке/разблокировке и транзакциях в соответствующие места.


Member 11040029

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

А Джон, что вы подразумеваете под попыткой обновить и оценить количество измененных записей? Вы имеете в виду обрабатывать этот счетчик на уровне интерфейса или что-то еще?

#realJSOP

Я обновляю свое решение, чтобы включить некоторые примеры SQL.

#realJSOP

Почему вы проголосовали 1 за мой ответ?

Member 11040029

Извиняюсь..это было просто для того, чтобы вы вернулись на этот пост как можно скорее! :) .. поднял его до 4

#realJSOP

Вы не используете голосование для этого.