Gibt es eine Möglichkeit zur Auswahl, ohne dass MySQL gesperrt wird?

126

Abfrage:

SELECT COUNT(online.account_id) cnt from online;

Die Online-Tabelle wird jedoch auch durch ein Ereignis geändert, sodass ich die Sperre häufig durch Ausführen sehen kann show processlist.

Gibt es eine Grammatik in MySQL, die dazu führen kann, dass eine select-Anweisung keine Sperren verursacht?

Und ich habe vergessen zu erwähnen, dass es sich um eine MySQL-Slave-Datenbank handelt.

Nachdem ich in my.cnf:transaction-isolation = READ-UNCOMMITTED den Slave hinzugefügt habe, wird ein Fehler auftreten:

Fehler 'Binäre Protokollierung nicht möglich. Nachricht: Die Transaktionsebene 'READ-UNCOMMITTED' in InnoDB ist für den Binlog-Modus 'STATEMENT' 'bei Abfrage nicht sicher

Gibt es eine kompatible Möglichkeit, dies zu tun?

Oh mein Gott
quelle
3
Für andere, die auf diese Frage stoßen und Schwierigkeiten mit den Sperren in ihren Tabellen haben: Wie mySQL Sperren intern verwendet, hängt von der Speicher-Engine ab. Lesen Sie die Antwort von @zombat unten.
Simon Forsberg

Antworten:

169

Fand einen Artikel mit dem Titel "MYSQL WITH NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

In MS SQL Server würden Sie Folgendes tun:

SELECT * FROM TABLE_NAME WITH (nolock)

und das MYSQL-Äquivalent ist

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

BEARBEITEN

Michael Mior schlug Folgendes vor (aus den Kommentaren)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;
Jon Erickson
quelle
54
Nur ein Hinweis für zukünftige Leser, den Sie möglicherweise entfernen möchten, SESSIONdamit die Transaktionsebene nur für die nächste Transaktion gilt. Ersetzen Sie dann einfach die dritte obige Aussage durch COMMIT. Dies ist in diesem Fall ein Noop, hat jedoch den Nebeneffekt, dass die Transaktion beendet und auf die Standardisolationsstufe zurückgesetzt wird.
Michael Mior
3
Nur eine Anmerkung, dieser Link ist tot ... :(
Longda
5
Entschuldigung, aber ich muss diese Antwort ablehnen, weil ich die sehr wichtigen Unterschiede zwischen InnoDB und MyISAM hier nicht erwähne. Wie oben von @omg angegeben, funktioniert dies für InnoDB, nicht jedoch für MyISAM-Tabellen.
Simon Forsberg
4
@Craig Es ist sicherlich ungenau, dass MyISAM bei SELECT-Abfragen keine READ-Sperren ausgibt - es gibt Sperren und im Gegensatz zu InnoDB sind diese Sperren Tabellensperren, die alle angeforderten WRITE-Sperren und alle nachfolgenden Abfragen während der Ausführung blockieren . Die ursprüngliche Frage scheint sich jedoch auf InnoDB zu beziehen, und Isolationsstufen sind auch für MyISAM nicht vorhanden - die Dokumente für den SET TRANSACTIONAnweisungsstatus : "Diese Anweisung legt die Transaktionsisolationsstufe fest, die für Operationen an InnoDB-Tabellen verwendet wird."
Syneticon-DJ
1
Punkt eingeräumt. :-) Ich habe wirklich versucht, mich auf das Sperrverhalten von MyISAM vs InnoDB zu beziehen. Diese auf Isolationsstufen basierenden Lösungen gelten nicht für MyISAM, das nicht transaktional ist. Daher wird eine einfache Tabellensperre verwendet. MyISAM UPDATE und DELETE müssen warten, bis die Tabellensperre aufgehoben ist, sodass alle nachfolgenden SELECT-Warteschlangen hinter der Schreibanforderung blockiert werden, bis der Schreibvorgang abgeschlossen ist. MyISAM hat keine "schmutzigen Lesevorgänge" und keine Möglichkeit, die meisten Schreibvorgänge gleichzeitig mit Lesevorgängen zuzulassen. Daher macht es keinen Sinn, sich hier mit Kommentaren zu befassen, "die MyISAM nicht ansprechen". Ich denke, das war es, worauf ich hinaus wollte. :-)
Craig
24

Wenn es sich bei der Tabelle um InnoDB handelt, lesen Sie http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html. Für SELECTs wird der konsistente Lesemodus (No-Locking-Modus) verwendet Geben Sie FOR UPDATE oder LOCK IN SHARE MODE nicht an, wenn die Option innodb_locks_unsafe_for_binlog festgelegt ist und die Isolationsstufe der Transaktion nicht auf SERIALIZABLE festgelegt ist. Daher werden keine Sperren für Zeilen festgelegt, die aus der ausgewählten Tabelle gelesen werden. "

Alex Martelli
quelle
16

Verwenden

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Version 5.0 Docs sind hier .

Version 5.1 Docs sind hier .

Nicht ich
quelle
2
Vielen Dank, ich denke, es ist nahe, aber wie lange wird diese Aussage wirksam sein? Ich werde diese Anweisung in einem PHP-Programm verwenden und sollte am besten automatisch auf TRANSACTION ISOLATION LEVEL zurückgesetzt werden, sobald die Abfrage beendet ist
omg
14

Vielleicht möchten Sie diese Seite des MySQL-Handbuchs lesen . Wie eine Tabelle gesperrt wird, hängt davon ab, um welchen Tabellentyp es sich handelt.

MyISAM verwendet Tabellensperren, um eine sehr hohe Lesegeschwindigkeit zu erreichen. Wenn jedoch eine UPDATE-Anweisung wartet, werden zukünftige SELECTS hinter dem UPDATE in die Warteschlange gestellt.

InnoDB-Tabellen verwenden Sperren auf Zeilenebene, und Sie müssen nicht die gesamte Tabelle hinter einem UPDATE sperren. Es gibt andere Arten von Sperrproblemen, die mit InnoDB verbunden sind, aber Sie werden möglicherweise feststellen, dass es Ihren Anforderungen entspricht.

Zombat
quelle
1
Funktioniert "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED" für MyISAM-Tabellen?
Omg
5
MyISAM-Tabellen unterstützen keine Transaktionen in irgendeiner Form. Eine Transaktionsabfrage wird in einer MyISAM-Tabelle ausgeführt, sodass die oben erwähnte Abfrage ausgeführt wird, jedoch keine Auswirkungen hat.
Zombat
1
Was kann ich dann tun, um zu vermeiden, dass SELECTS bei MyISAM ansteht?
Omg
6
Was kann ich tun, um zu vermeiden, dass SELECTS bei MyISAM ansteht? Wechseln Sie zu innodb. MyISAM verwendet für jede Abfrage Sperren auf Tabellenebene. Das ist ein großer Fehler.
Frank Farmer
2

Abhängig von Ihrem Tabellentyp funktioniert das Sperren unterschiedlich, aber auch die Anzahl der SELECT. Bei MyISAM-Tabellen sollte eine einfache SELECT count (*) FROM-Tabelle die Tabelle nicht sperren, da sie auf Metadaten zugreift, um die Datensatzanzahl abzurufen. Innodb wird länger dauern, da es die Tabelle in einem Snapshot erfassen muss, um die Datensätze zu zählen, aber es sollte keine Sperrung verursachen.

Sie sollten mindestens concurrent_insert auf 1 gesetzt haben (Standard). Wenn die Datendatei keine "Lücken" für die zu füllende Tabelle enthält, werden Einfügungen an die Datei angehängt, und SELECT und INSERTs können gleichzeitig mit MyISAM-Tabellen erfolgen. Beachten Sie, dass beim Löschen eines Datensatzes eine "Lücke" in der Datendatei entsteht, die versucht, mit zukünftigen Einfügungen und Aktualisierungen gefüllt zu werden.

Wenn Sie Datensätze selten löschen, können Sie concurrent_insert auf 2 setzen. Einfügungen werden immer am Ende der Datendatei hinzugefügt. Dann können Auswahlen und Einfügungen gleichzeitig erfolgen, aber Ihre Datendatei wird niemals kleiner, unabhängig davon, wie viele Datensätze Sie löschen (mit Ausnahme aller Datensätze).

Unter dem Strich sollten Sie, wenn Sie viele Aktualisierungen, Einfügungen und Auswahlen in einer Tabelle haben, diese zu InnoDB machen. Sie können Tabellentypen in einem System jedoch frei mischen.

Brent Baisley
quelle
0

Aus dieser Referenz:

Wenn Sie eine Tabellensperre explizit mit LOCK TABLES erwerben, können Sie eine READ LOCAL-Sperre anstelle einer READ-Sperre anfordern, damit andere Sitzungen gleichzeitig Einfügungen durchführen können, während Sie die Tabelle gesperrt haben.

Paul Sonier
quelle
0

SELECTs führen normalerweise keine Sperren durch, die Sie für InnoDB-Tabellen interessieren. Die Standard-Transaktionsisolationsstufe bedeutet, dass bei Auswahlen keine Inhalte gesperrt werden.

Natürlich kommt es immer noch zu Streitigkeiten.

MarkR
quelle
1
Ich weiß, dass dieser Beitrag alt ist, aber diese Antwort ist zu allgemein und nur manchmal wahr. Siehe dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html . Schlösser mit Sicherheit wird erworben für liest, abhängig von der Isolationsstufe. Insbesondere in diesem Fall befasst sich das Poster mit replizierten Datenbanken und gibt ausdrücklich an, dass er show processlistdie Sperren tatsächlich sehen kann. Man kann also davon ausgehen, dass tatsächlich Sperren genommen werden.
Craig
1
Die Antwort ist immer wahr. Es gibt natürlich einige Sperren - einige interne Mutexe in innodb, die verwendet werden (z. B. innodb buffer pool mutex). Die meisten Benutzer interessieren sich nicht für diese Sperren oder bemerken sie nicht und sie treten normalerweise nur während DDL-Vorgängen an (z. B. wenn Sie einen 16G-Pufferpool haben und "Tabelle löschen" in einem anderen Thread ausführen). Standardmäßig sind jedoch keine Zeilensperren erforderlich. Das ist es was ich meinte. Die Antwort war allerdings ziemlich vage.
MarkR
1
Immer immer? Was passiert, wenn die Transaktionsisolationsstufe auf serialisierbar eingestellt ist oder die select-Anweisung LOCK IN SHARE MODE verwendet und die automatische Festschreibung deaktiviert ist? Ich weiß, dass viele (die meisten / alle?) Datenbankserver jetzt standardmäßig die Snapshot-Isolation anstelle der echten Serialisierung verwenden, aber gibt es nicht immer noch gelegentliche Gründe, um serialisierbare Lesevorgänge zu erzwingen? Aber es hört sich so an, als hätten Sie gesagt, dass die Standardbedingungen in MySQL in allen normalen Fällen keine Lesesperren verursachen, die sich auf andere Threads auswirken. Machen Sie sich also keine Sorgen über ein Problem, das Sie nicht haben? Ich habe versucht, meine Ablehnung rückgängig zu machen, übrigens. Entschuldigung ...
Craig
3
Ich sagte "nicht normal". Ich meinte, wenn Sie eine normale Auswahl treffen (ohne FOR UPDATE oder LOCK IN SHARE MODE) und die Standard-Transaktionsisolationsstufe verwenden. Es gibt einige gültige Fälle zum Ändern der Isolationsstufe, aber ich würde dies nur pro Sitzung tun, niemals die Standardeinstellung.
MarkR
0

Eine andere Möglichkeit, das Dirty Read in MySQL zu aktivieren, ist das Hinzufügen eines Hinweises: LOCK IN SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 
PuGong
quelle
"Wenn Autocommit auf 1 gesetzt ist, haben die Klauseln LOCK IN SHARE MODE und FOR UPDATE keine Auswirkung." ... und autocommit = 1 ist Standard
Martin Zvarík