web-dev-qa-db-fra.com

Blocage mysql / innodb sur simple requête de suppression

Mysql/innodb 8.0.16, lire les transactions validées, plusieurs instructions dans la transaction soulèvent un blocage lors de la suppression des lignes non interceptantes. Essayer de comprendre ce qui se passe:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-05-14 21:57:44 0x7fe9546c6700
*** (1) TRANSACTION:
TRANSACTION 2852, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 14 row lock(s), undo log entries 25
MySQL thread id 146, OS thread handle 140640122267392, query id 1586 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7533
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2852 lock_mode X locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) TRANSACTION:
TRANSACTION 2853, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
8 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 12
MySQL thread id 147, OS thread handle 140640120497920, query id 1594 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7534
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 96 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 32
 0: len 4; hex 80006502; asc   e ;;
 1: len 6; hex 000000000b24; asc      $;;
 2: len 7; hex 0200000128012d; asc     ( -;;
 3: len 4; hex 80001d6d; asc    m;;
 4: len 4; hex 80109d40; asc    @;;
 5: len 4; hex 800023ba; asc   # ;;
 6: len 4; hex 800098e6; asc     ;;
 7: len 4; hex 80000007; asc     ;;
 8: len 9; hex 466f696c20676c7565; asc Foil glue;;
 9: len 8; hex 0000000000005940; asc       Y@;;
 10: len 1; hex 4d; asc M;;

*** WE ROLL BACK TRANSACTION (2)

Structure du tableau:

    CREATE TABLE `deal_product_rows_tmp` (
    `batch_no` int(11) NOT NULL,
    `bitrix_id` int(11) NOT NULL,
    `deal_id` int(11) NOT NULL,
    `product_id` int(11) NOT NULL,
    `quantity` int(11) NOT NULL,
    `product_name` varchar(1000) NOT NULL,
    `price` double DEFAULT NULL,
    `status` varchar(50) NOT NULL,
    `tmp_id` int(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`tmp_id`),
    KEY `idx_deal_row_tmp_deal` (`deal_id`),
    KEY `idx_deal_row_tmp_batchno` (`batch_no`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7500 DEFAULT CHARSET=utf8 

1) les requêtes suppriment des lignes séparées, comment est-il possible d'enregistrer des verrous sur la même ligne?

2) même si les transactions appliquent un verrouillage sur la même ligne, pourquoi un blocage? pourquoi la première transaction ne peut pas attendre la deuxième libération du verrou?

UPDATE1: Logique d'application - application de démarrage de printemps recevant un appel de repos du système crm, récupérant les lignes de produit de commande client (deal) et essayant de synchroniser les données dans micro dwh. Il y a plusieurs déclarations dans une transaction RC, un seul type de transaction implémenté dans cette application. Première instruction REPLACE INTO deal table, cela devrait empêcher le traitement de lots sur le même deal_id en parallèle (clés FK non utilisées pour le moment). Ensuite, la transaction insère les lignes de produits reçues dans la table "tmp" et compare sur la table "dwh" cible. insertion de produits manquants, mise à jour du statut des lignes de produits "supprimées". Étape suivante: suppression des données "tmp" de deal_product_rows_tmp (point de blocage) et étape finale - marquer le lot comme terminé (mettre à jour l'état du jeu de lots = .. table où batch_no =?)

3
Triffids

Normalement, les gens sont confus à propos de verrouillage de l'espace et du verrouillage supplémentaire dû à une mauvaise indexation sur la table entière ou à plus d'enregistrements que prévu, mais ce n'est pas votre cas:

  • Vous avez l'index secondaire approprié
  • Les verrous déclarent qu'aucun verrouillage d'espace n'est nécessaire (comme prévu)
  • Les enregistrements verrouillés/recherchés sont différents
  • Je ne peux pas reproduire le problème à moins de supprimer le idx_deal_row_tmp_batchno index
  • Les enregistrements sont sur la même page, mais ils ne devraient pas, en théorie, s'influencer mutuellement

D'après les informations limitées, ce que je peux deviner est:

  • Vous supprimez plusieurs enregistrements dans un lot (car l'un contient 3 verrous de ligne et l'autre 14)
  • Vous rencontrez une condition de concurrence critique, où la transaction 1 est capable de verrouiller batch_no=7533 enregistrer, et la transaction 2 peut verrouiller batch_no=7534, mais ils souhaitent également mettre à jour sur des requêtes distinctes (mais la même transaction) les autres enregistrements
  • une dépendance est détectée et InnoDB tue la plus récente pour éviter une boucle infinie (blocage)

Ils ne peuvent pas simplement attendre, car sinon ce serait une attente infinie (car ils dépendent les uns des autres). S'il ne s'agissait pas d'un cycle, la deuxième transaction attendrait en effet jusqu'à innodb_lock_wait_timeout secondes.

Il existe plusieurs stratégies:

  • Surveillez les blocages, s'ils ne sont pas courants, ne faites rien (réessayez sur l'application). Tant que les erreurs ne se produisent pas fréquemment, elles ne seront pas une énorme pénalité
  • Modifiez votre stratégie de mise à jour pour éviter les collisions (par exemple, supprimez dans un seul lot plus volumineux, même si cela peut augmenter la latence), supprimez une ligne par transaction, coordonnez l'application pour la suppression, etc.
  • Utilisez les nouvelles fonctionnalités 8.0 SKIP LOCKED et NOWAIT pour ignorer les lignes immédiatement verrouillées, spécialement pour les processus par lots: https://mysqlserverteam.com/mysql-8-0-1-using-skip-locked-and-nowait -à-gérer-lignes-chaudes /
4
jynus