Arbeiten rund um MySQL-Fehler „gefunden Deadlock, wenn Sperre zu bekommen versuchen; versuchen einen Neustart Transaktion“

StackOverflow https://stackoverflow.com/questions/2596005

Frage

Ich habe eine MySQL-Tabelle mit etwa 5.000.000 Zeilen, die über DBI in kleinen Wegen durch parallele Perl Prozesse verbinden ständig aktualisiert werden. Die Tabelle hat etwa 10 Spalten und mehrere Indizes.

Eine recht häufige Operation führt zu folgendem Fehler manchmal:

DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction at Db.pm line 276.

Die SQL-Anweisung, dass Auslöser der Fehler so etwas wie diese:

UPDATE file_table SET a_lock = 'process-1234' WHERE param1 = 'X' AND param2 = 'Y' AND param3 = 'Z' LIMIT 47

Der Fehler wird nur manchmal ausgelöst. Ich würde in 1% der Anrufe oder weniger schätzen. Es ist jedoch nie mit einem kleinen Tisch passiert ist und hat sich häufiger als die Datenbank gewachsen ist.

Beachten Sie, dass ich das a_lock Feld in file_table bin mit, um sicherzustellen, dass die vier nahezu identische Prozesse bei mir läuft nicht versuchen und arbeiten in der gleichen Zeile. Die Grenze ist für ihre Arbeit in kleine Stücke zu brechen.

Ich habe nicht viel Stimmung auf MySQL oder DBD :: mysql getan. MySQL ist eine Standard-Solaris-Implementierung und die Datenbankverbindung wird wie folgt festgelegt:

my $dsn = "DBI:mysql:database=" . $DbConfig::database . ";host=${DbConfig::hostname};port=${DbConfig::port}";
my $dbh = DBI->connect($dsn, $DbConfig::username, $DbConfig::password, { RaiseError => 1, AutoCommit => 1 }) or die $DBI::errstr;

Ich habe online gesehen, dass mehrere andere Menschen Ähnliche Fehler gemeldet haben, und dass dies eine echte Deadlock-Situation sein.

Ich habe zwei Fragen:

  1. Was genau über meine Situation verursacht den Fehler oben?

  2. Gibt es eine einfache Möglichkeit, um es an der Arbeit oder zur Milderung seiner Frequenz? Zum Beispiel, wie genau gehe ich über die „Transaktion bei Db.pm Linie Neustart 276“?

Vielen Dank im Voraus.

War es hilfreich?

Lösung

Wenn Sie mit InnoDB oder jede Zeilenebene Transaktions RDBMS, dann ist es möglich, dass jeder Schreibtransaktion zu einem Deadlock führen kann, auch in ganz normalen Situationen. Größere Tabellen, größere schreibt, und lange Transaktion blockiert werden oft die Wahrscheinlichkeit von Deadlocks erhöhen auftreten. In Ihrer Situation ist es wahrscheinlich eine Kombination davon.

Der einzige Weg, wirklich Deadlocks handhaben ist Ihr Code zu schreiben, um sie zu erwarten. Dies ist im Allgemeinen nicht sehr schwierig, wenn Sie Ihr Datenbank-Code ist gut geschrieben. Oft können Sie eine try/catch um die Abfrage-Ausführungslogik gerade auf und für eine Sackgasse aussehen, wenn Fehler auftreten. Wenn Sie ein zu fangen, wird die normale Sache zu tun versuchen, nur die ausgefallene Abfrage erneut auszuführen.

Ich empfehle Ihnen, lesen Sie diese Seite das MySQL-Handbuch. Es hat eine Liste der Dinge zu Hilfe zu tun mit Deadlocks zu bewältigen und ihre Häufigkeit reduzieren.

Andere Tipps

Die Antwort richtig ist, aber die Perl-Dokumentation, wie Deadlocks zu handhaben ist ein spärliche bisschen und vielleicht verwirrend mit Print, Raiseerror und Handeln Optionen. Es scheint, dass anstatt sich mit Handle, den Einsatz auf Druck und Raise und dann etwas verwenden wie Versuchen: Tiny Code wickeln und auf Fehler überprüfen. Der Code unten gibt ein Beispiel, wo der db-Code in einem while-Schleife ist, dass eine fehlerhafte SQL-Anweisung alle 3 Sekunden erneut ausgeführt wird. Der catch-Block wird _ $, welche die spezifische err Nachricht ist. Ich gehe dies zu einer Behandlungsfunktion „dbi_err_handler“, die 1 $ Kontrollen _ gegen eine Vielzahl von Fehlern und kehrt, wenn der Code (und dadurch bricht die Schleife) oder 0, wenn es ein Deadlock fortgesetzt werden sollte und sollte erneut versucht ...

$sth = $dbh->prepare($strsql);
my $db_res=0;
while($db_res==0)
{
   $db_res=1;
   try{$sth->execute($param1,$param2);}
   catch
   {
       print "caught $_ in insertion to hd_item_upc for upc $upc\n";
       $db_res=dbi_err_handler($_); 
       if($db_res==0){sleep 3;}
   }
}

dbi_err_handler sollte die folgende zumindest haben:

sub dbi_err_handler
{
    my($message) = @_;
    if($message=~ m/DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction/)
    {
       $caught=1;
       $retval=0; # we'll check this value and sleep/re-execute if necessary
    }
    return $retval;
}

Sie sollten andere Fehler enthalten, die Sie wünschen, zu handhaben und Satz Retval $ je nachdem, ob Sie möchten erneut ausführen oder fortsetzen ..

Hope, das hilft jemand -

Beachten Sie, dass, wenn Sie SELECT FOR UPDATE verwenden, um eine Einzigartigkeit Prüfung vor einem Einsatz durchzuführen, einen Deadlock für jedes Rennen Zustand erhalten wird, wenn Sie die innodb_locks_unsafe_for_binlog Option aktivieren. Ein Deadlock freie Methode Eindeutigkeit zu überprüfen ist, blind Einsatz einer Reihe in eine Tabelle mit einem eindeutigen Index INSERT IGNORE verwendet, dann die betroffene Zeilenanzahl zu überprüfen.

Add unterhalb der Linie zu my.cnf Datei

innodb_locks_unsafe_for_binlog = 1

#

1 - ON
0 - OFF

#

Die Idee, die Abfrage bei Deadlock Ausnahme von Neuem Versuch ist gut, aber es kann schrecklich langsam sein, da mysql Abfrage warten läßt für Sperren freigegeben werden. Und Incase Deadlock mysql versucht, wenn zu finden es eine Sackgasse ist, und selbst nachdem sie herausgefunden, dass es eine Sackgasse ist, wartet er eine Weile, bis ein Gewinde, um von Deadlock-Situation raus tritt aus.

Was ich tat, als ich diese Situation konfrontiert ist, in Ihrem eigenen Code zu implementieren sperren, da es der Verriegelungsmechanismus von MySQL ist, aufgrund eines Fehlers fehlschlägt. Also habe ich meine eigene Sperren auf Zeilenebene in meinem Java-Code implementiert:

private HashMap<String, Object> rowIdToRowLockMap = new HashMap<String, Object>();
private final Object hashmapLock = new Object();
public void handleShortCode(Integer rowId)
{
    Object lock = null;
    synchronized(hashmapLock)
    {
      lock = rowIdToRowLockMap.get(rowId);
      if (lock == null)
      {
          rowIdToRowLockMap.put(rowId, lock = new Object());
      }
    }
    synchronized (lock)
    {
        // Execute your queries on row by row id
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top