Обе транзакции ждут, когда ресурс станет доступным, и никогда не снимают удерживаемые им блокировки.

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

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

Mysql выполняет блокировку при каждом обновлении или удалении и обычно блокирует набор записей, сканируемых в индексе, при обработке оператора SQL. InnoDB не заботится о предложении where, только о сканированном диапазоне индексов.

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

Client_Days table
+-----------+------------+---------|
| Client_Id |    Day     | Value_A |
+-----------+------------+---------+
| ABC10     | 2018-05-01 |       3 |
| 05BCA     | 2018-07-23 |       4 |
+-----------+------------+---------+
UNIQUE KEY Client_Id_Day (Client_id, Day)

Устанавливается первое соединение для обработки следующего оператора:

INSERT INTO 
    Client_Days (Client_Id, Day, Value_A)
        ('ABC10', '2018-08-01', 5),
        ('ABC10', '2018-06-01', 7)  
ON DUPLICATE KEY UPDATE 
    Value_A = VALUES(Value_A);

MySql заблокирует разрыв между 2018–07–23 и 2018–08–01, а затем заблокирует разрыв между 2018–05–01 и 2018–06–01.

Если в то же время происходит второе соединение для выполнения оператора:

INSERT INTO 
    Client_Days (Client_Id, Day, Value_A)
        ('ABC10', '2018-07-11', 6)
        ('ABC10', '2018-07-25', 6) 
ON DUPLICATE KEY UPDATE 
    Value_A = 6;

У нас будет тупик, первое соединение ожидает разблокировки между 2018–05–01 и 2018–07–11, чтобы вставить значение в 2018–06–01.

Второе подключение ожидает разблокировки первого подключения в промежутке между 2018–07–23 и 2018–08–01, чтобы вставить значение 2018–07–25.

Итак, они оба ждут друг друга.

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

Если тупики случаются так часто, вот вам несколько советов:

  • Если возможно, используйте систему очереди брокера сообщений fifo за базой данных. В приведенном ниже примере очередь для каждого идентификатора клиента разрешит тупиковые ситуации, транзакции будут выполняться последовательно.
  • Реализуйте механизм повтора, который должен решить проблему.
  • Транзакции должны быть простыми и быстрыми, чтобы избежать коллизий.
  • Разумно создавайте индексы в своих таблицах, чтобы запросы сканировали меньше записей, следовательно, устанавливали меньше блокировок.

Если вам понравилась эта статья, порекомендуйте и поделитесь.