Прежде всего, получение ошибки дублированного ключа (или, например, взаимных блокировок) время от времени не является принципиально проблематичным c. Серверы баз данных по своей сути параллельны, и если вы не хотите полностью блокировать базу данных, ожидается, что два процесса могут попытаться сделать что-то, что конфликтует друг с другом (например, попытка вставить одну и ту же строку). Он пойман, и ваша задача теперь реагировать на это должным образом.
В вашей ситуации вы должны получить сообщение об ошибке, когда у вас нет строк для удаления (например, идентификатор еще не существует), что особенно вероятно, если вы используете свой логин c для удаления-вставки в целом даже для совершенно новых записей.
Уровень изоляции read committed
не сохраняет блокировку для несуществующих строк (или блокировку пробела):
Для Операторы UPDATE или DELETE, InnoDB удерживает блокировки только для строк, которые он обновляет или удаляет. Блокировки записей для несовпадающих строк снимаются после того, как MySQL оценил условие WHERE. Это значительно снижает вероятность тупиков, но они все же могут возникнуть.
Таким образом, без существующей строки для удаления может иногда происходить следующий поток для двух транзакций A и B, которые хотят вставить один и тот же идентификатор:
A: delete id, no rows -> no lock
B: delete id, no rows -> no lock (and no wait)
A: insert id -> locks row(s)
B: insert id -> wait
A: commit
B: lock released -> insert id -> duplicate key error
Ваши данные могут отличаться , вы можете использовать некоторую форму, которая скрывает эти детали, или, например, у вас может быть дополнительная проверка, чтобы увидеть, существует ли уже этот идентификатор (но затем вы должны включить эту проверку / выбрать с for update
в вашей транзакции, чтобы сделать эту проверку согласованной), и затем используйте другую процедуру, которая просто вставляет - но в этом случае вы просто столкнетесь с подобной проблемой: вы просто получите ошибку дублирующего ключа в этой другой процедуре (где две «простые» вставки конфликтуют друг с другом).
В конечном счете, вам просто нужно разобраться со случаем, когда две сессии пытаются сделать одно и то же одновременно. Поскольку вы исключили все решения, которые обрабатывают эту ситуацию внутри базы данных (например, replace
или on duplicate key update
), вам придется отлавливать и обрабатывать его вручную в своем приложении (например, откат и повтор транзакции).
Кстати, вы можете проверить это, изменив уровень изоляции на repeatable read
(но поскольку это может быть значительным изменением, вы должны делать это только в среде разработки). Это должно привести к тупику, а не к ошибке дублированного ключа в этой ситуации. Хотя это не совсем решит вашу проблему, это может убедить вас, что это действительно проблема.