MySql Gap Lock Deadlock on Inserts


8

Ricevo deadlock dai blocchi gap su un tavolo quando lo inserisco frequentemente da più fonti. Ecco una panoramica dei miei processi.

START TRANSACTION
  UPDATE vehicle_image
  SET active = 0
  WHERE vehicleID = SOMEID AND active = 1

  Loop:
    INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath
      ,vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
    VALUES (%s, %s, %s, %s, %s, %s, 1);
END TRANSACTION

L'output di SHOW Create table vehicle_image;è:

CREATE TABLE `vehicle_image` (
  `vehicleImageID` int(11) NOT NULL AUTO_INCREMENT,
  `vehicleID` int(11) DEFAULT NULL,
  `vehicleImageFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageSplashFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageThumbnailFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageMiniFilePath` varchar(200) DEFAULT NULL,
  `mainVehicleImage` bit(1) DEFAULT NULL,
  `active` bit(1) DEFAULT b'1',
  `userCreated` int(11) DEFAULT NULL,
  `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `userModified` int(11) DEFAULT NULL,
  `dateModified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`vehicleImageID`),
  KEY `active` (`active`),
  KEY `mainvehicleimage` (`mainVehicleImage`),
  KEY `vehicleid` (`vehicleID`)
) ENGINE=InnoDB AUTO_INCREMENT=22878102 DEFAULT CHARSET=latin1

E l'ultimo deadlock dato da SHOW engine innodb status:

LATEST DETECTED DEADLOCK
------------------------
2018-03-27 12:31:15 11a58
*** (1) TRANSACTION:
TRANSACTION 5897678083, ACTIVE 2 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873570, OS thread handle 0x124bc, query id 198983754 ec2-34-239-240-179.compute-1.amazonaws.com 34.239.240.179 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006176, 'f180928(1)1522168276.230837full.jpg', 'f180928(1)1522168276.230837splash.jpg', 'f180928(1)1522168276.230837thumb.jpg', 'f180928(1)1522168276.230837mini.jpg', 1, 1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678083
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) TRANSACTION:
TRANSACTION 5897678270, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873571, OS thread handle 0x11a58, query id 198983849 ec2-35-171-169-21.compute-1.amazonaws.com 35.171.169.21 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006326, '29709(1)1522168277.4443843full.jpg', '29709(1)1522168277.4443843splash.jpg', '29709(1)1522168277.4443843thumb.jpg', '29709(1)1522168277.4443843mini.jpg', 1, 1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 875 page no 238326 n bits 464
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** WE ROLL BACK TRANSACTION (2)

Sto eseguendo molti di questi processi contemporaneamente ma mai eseguendo due processi che utilizzano lo stesso VehicleID. Sono davvero confuso sul motivo per cui sto ottenendo deadlock.

Ho temporaneamente risolto il problema utilizzando il livello di isolamento READ COMMITTED, ma ho letto che ciò richiede modifiche alla replica in quanto è necessario eseguire la replica a livello di riga.

Ho letto qui altre domande simili alle mie, ma sono un po 'nuovo su SQL e non riesco ancora a capire perché questo accada.

Domande simili:
- Deadlock su istruzioni di inserimento MySQL
- MySQL InnoDB Deadlock Per 2 semplici query di inserimento

AGGIORNARE:

Ho scoperto che l'utilizzo READ COMMITTEDnon ha risolto il problema. Non ho ancora capito perché si stiano verificando i deadlock e non so davvero come diagnosticare oltre quello che ho attualmente. Sto continuando a ottenere deadlock nel mio sistema di produzione. Qualsiasi aiuto sarebbe apprezzato.


Potresti fornirci qualche dettaglio in più - in particolare: configurazione del disco, numero di piatti, caratteristiche prestazionali? RAM, quanto? CPU, numero e prestazioni? Numero di transazioni al secondo, al minuto, all'ora e al giorno? Queste tariffe variano nel tempo? In che modo esattamente questi deadlock influiscono sulle prestazioni? Dacci l'output di SHOW PROCESSLIST;. Il più delle volte REPEATABLE READè il miglior livello di isolamento per la maggior parte delle app, quindi non mi preoccuperei troppo di usarlo. C'è stato un notevole aumento delle prestazioni quando lo hai cambiato dal valore predefinito - REPEATABLE READ?
Vérace,

Come può funzionare? Hai inserito stringhe non quotate VARCHARs.
Rick James,

Dov'è END LOOP?
Rick James,

@RickJames Non ho stringhe non quotate in VARCHARS, le query funzionano come previsto se eseguite il 95% delle volte. END LOOP è indicato dalla tabulazione allo stesso livello. Ad esempio, il ciclo inizia, eseguo l'istruzione insert più volte, quindi il ciclo termina e la transazione termina. Si noti che loopè solo pseudocodice per rappresentare ciò che sta accadendo.
Brian Sizemore,

@ Vérace Repeatable Read è l'impostazione predefinita per questa tabella (utilizzando il motore innodb). Io in realtà provato trasformandolo da repeatable reada read committedche è un livello più basso di isolamento allora lettura ripetibile, ma purtroppo non ha fermato il deadlock. So che l'hardware influenzerà il server (è un'istanza ec2, dovrei cercare specifiche) ma non penso che tali informazioni sarebbero necessarie per capire perché si verificano i deadlock. La natura sporadica di questo rende anche difficile catturare l'output di show process list; quando si verifica lo stallo.
Brian Sizemore,

Risposte:


4

Non sono un esperto di MySQL, ma dall'aspetto dei tuoi registri Deadlock, anche se INSERISCI ID veicolo diversi per istruzione, questi richiedono che l' intera pagina dati (238326) dell'indice VehicleIDnon cluster sia bloccata .

Il fatto che occasionalmente si ottengano deadlock significa che entro 1 pagina si hanno più ID veicolo, quindi c'è una piccola possibilità che 2 processi diversi necessitino di un blocco per la stessa pagina.

La cosa migliore da consigliare è di mantenere le transazioni il più piccolo possibile .

Se c'è un modo per fare quanto segue, ciò contribuirà a ridurre la possibilità di un deadlock:

START TRANSACTION;
  UPDATE vehicle_image SET active = 0 WHERE vehicleID = SOMEID and active = 1;
END TRANSACTION;
Loop:
  START TRANSACTION;
  INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath,
    vehicleImageSplashFilePath, vehicleImageThumbnailFilePath,
    vehicleImageMiniFilePath, mainVehicleImage, active)
  VALUES (%s, %s, %s, %s, %s, %s, 1);  
  END TRANSACTION;
--EndLoop here

Se puoi, prova a cambiare il fattore di riempimento di quell'indice al 95% e prova a vedere se hai meno deadlock.

Un test più estremo sarebbe quello di rimuovere completamente quell'indice durante INSERT, quindi ricrearlo al termine.


Hai qualche idea sul perché l'intera pagina sarebbe bloccata rispetto solo alla riga / e che sto cercando di inserire?
Brian Sizemore,

1
Inoltre, ho intenzione di riformattare un po 'il mio codice e ridurre i tempi di transazione. Credo che tu abbia ragione che dovrebbe fare una differenza significativa.
Brian Sizemore,

Non sono sicuro di come funzionano gli interni di MySQL, ma questa risposta lo spiega per MS SQL. Alcuni buoni consigli MySQL anche nel Manuale MySQL .
Oreo,

1
MySQL non fornisce alcun controllo sul fattore di riempimento.
Rick James,

2
Dopo aver rifattorizzato il mio codice per mettere in coda i miei inserti e la dichiarazione di aggiornamento e averli eseguiti molto vicini tra loro, ha risolto il mio problema. Non solo, ma sono stato in grado di continuare a ridimensionarlo (circa il doppio della precedente quantità di processi paralleli) e funziona ancora correttamente. Grazie Oreo!
Brian Sizemore,

1

MySQL non solo blocca la riga interessata, ma anche la riga dell'indice interessata e lo spazio tra le righe dell'indice (come descritto qui ). Poiché le chiavi primarie sono sempre indicizzate e le usi nei tuoi aggiornamenti, sospetto che più transazioni che provano ad aggiornare più righe provochino blocchi di gap dell'indice sovrapposti che a loro volta creano il deadlock.

Per risolvere questo, consiglio anche a Oreos di mantenere una transazione il più piccola possibile. Se le righe aggiornate sono indipendenti l'una dall'altra, è necessario utilizzare una transazione separata per ognuna di esse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.