MySQL / MariaDB InnoDB Одновременные транзакции и поведение блокировки - PullRequest
0 голосов
/ 25 июня 2019

В рамках процесса сохранения в одной из моих моделей генерируется контрольная сумма md5 всей записи и сохраняется вместе с записью.Контрольная сумма md5 содержит сжатое представление всей записи, включая все атрибуты EAV и т. Д. Это делает предотвращение абсолютных дубликатов очень простым и эффективным.

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

Я использую Eloquent от Laravel.Поэтому, как только запись была создана и перед фиксацией приложения делает следующее:


            $taxonRecords = TaxonRecord::where('check_sum', $taxonRecord->check_sum)->get();

            if ($taxonRecords->count() > 0) {
                DB::rollBack();
                return $taxonRecords->first();
            }

Однако недавно я столкнулся с инцидентом 60 000/1 выстрела (шансы, основанные на количестве записей в то время).Один дубликат попал в базу данных с той же контрольной суммой.Просматривая журналы, я заметил, что время создания было идентичным с точностью до секунды.Дальнейшее изучение журналов Apache показало действительный POST, но POST был продублирован.Я предполагаю, что пользовательский браузер неисправен или что-то в этом роде, но оба сообщения POSTS пришли одновременно, что привело к двум одновременным транзакциям.

Мой вопрос состоит в том, как я могу убедиться, что транзакция и ее значение SELECT для предыдущей контрольной суммы равно Atomic & Isolated.Исходя из моего прочтения, ответ лежит в https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html и уровнях изоляции.

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

1 Ответ

0 голосов
/ 26 июня 2019

Вы создали классическое состояние гонки .Обе транзакции вычисляют контрольную сумму, пока они обе находятся в процессе выполнения, еще не зафиксированы.Ни один из них не может прочитать данные другого, так как они не переданы.Поэтому они рассчитывают, что они единственные с одинаковой контрольной суммой, и они оба проходят и фиксируют.

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

Возможно, вам придется использовать GET_LOCK () перед началом транзакции для вычисления контрольной суммы.Затем RELEASE_LOCK () после фиксации.Это гарантирует, что другие параллельные запросы будут ожидать ваших данных, поэтому они увидят их, когда попытаются вычислить контрольную сумму.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...