Warum speichert InnoDB die Zeilenanzahl nicht?

19

Jeder weiß, dass in Tabellen, die InnoDB als Engine verwenden, Abfragen wie SELECT COUNT(*) FROM mytablesehr ungenau und sehr langsam sind, insbesondere wenn die Tabelle größer wird und während der Ausführung dieser Abfrage ständig Zeilen eingefügt / gelöscht werden.

Wie ich es verstanden habe, speichert InnoDB die Zeilenzahl nicht in einer internen Variablen, was der Grund für dieses Problem ist.

Meine Frage ist: Warum ist das so? Wäre es so schwer, solche Informationen zu speichern? Es ist eine wichtige Information, in so vielen Situationen Bescheid zu wissen. Die einzige Schwierigkeit, die ich sehe, wenn eine solche interne Zählung implementiert würde, ist, wenn Transaktionen beteiligt sind: Wenn die Transaktion nicht festgeschrieben ist, zählen Sie die von ihr eingefügten Zeilen oder nicht?

PS: Ich bin kein Experte für DBs, ich bin nur jemand, der MySQL als einfaches Hobby hat. Also, wenn ich nur etwas Dummes gefragt habe, sei nicht übermäßig kritisch: D.

Radu Murzea
quelle
6
Langsam, ja. Ungenau, nein. Es ist langsam, weil es das genaue Ergebnis liefert. Wenn Sie eine Tabelle mit 200 Millionen Zeilen und möglicherweise viele andere Transaktionen haben, die in dieselbe Tabelle einfügen / löschen, möglicherweise viele Zeilen pro Sekunde, lautet eine andere Frage: "Benötigen Sie die genaue Nummer?"
ypercubeᵀᴹ
@ypercube Ich weiß, dass ich in phpmyadmin einige Zeilenanzahlwerte gesehen habe, die sehr unterschiedlich waren. Außerdem gibt es dort einen Kommentar, in dem etwas wie "möglicherweise nicht genau" steht.
Radu Murzea
1
@RaduMurzea phpMyAdmin-Benutzer haben eine alternative Methode zum Berechnen der Tabellenzahlen für InnoDB-Tabellen aus Gründen der Geschwindigkeit, die Sie kennen. Hier kommt die von Ihnen erwähnte Ungenauigkeit ins Spiel. Aktuelle SELECT COUNT(*) FROM ...Abfragen sind präzise. Wenn Sie möchten, kann phpMyAdmin so konfiguriert werden, dass auf Kosten der Geschwindigkeit immer exakte Zeilenzahlen verwendet werden. Weitere Informationen: stackoverflow.com/questions/11926259/…
DOOManiac

Antworten:

9

Ich stimme @RemusRusanu zu (+1 für seine Antwort)

SELECT COUNT(*) FROM mydb.mytablein InnoDB verhält sich wie eine Transaktionsspeicher-Engine. Vergleichen Sie es mit MyISAM.

MyISAM

Wenn mydb.mytablees sich um eine MyISAM-Tabelle handelt, ist das Starten SELECT COUNT(*) FROM mydb.mytable;wie das Ausführen SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Dies löst eine schnelle Suche nach der Zeilenzahl im Header der MyISAM-Tabelle aus.

InnoDB

Wenn mydb.mytablees sich um einen InnoDB-Tisch handelt, ist einiges los. Sie haben MVCC und regeln Folgendes:

  • ib_logfile0 / ib_logfile1 (Redo Logs)
  • ibdata1
    • Protokolle rückgängig machen
    • Rollbacks
    • Änderungen im Datenwörterbuch
  • Pufferpool-Verwaltung
  • Transaktionsisolation (4 Arten)
    • Wiederholbare Lesevorgänge
    • Lesen Sie Committed
    • Read Uncommitted
    • Serialisierbar

Wenn Sie InnoDB nach einer Tabellenzahl fragen, müssen Sie durch diese bedrohlichen Dinge navigieren. Tatsächlich weiß man nie wirklich, ob SELECT COUNT(*) from mydb.mytablenur wiederholbare Lesevorgänge gezählt werden oder ob Lesevorgänge, die festgeschrieben wurden, und solche, die nicht festgeschrieben wurden, eingeschlossen sind.

Sie könnten versuchen, die Dinge ein wenig zu stabilisieren, indem Sie innodb_stats_on_metadata aktivieren .

Gemäß der MySQL-Dokumentation zu innodb_stats_on_meta_data

Wenn diese Variable aktiviert ist (dies ist wie vor der Erstellung der Variablen die Standardeinstellung), aktualisiert InnoDB Statistiken während Metadatenanweisungen wie SHOW TABLE STATUS oder SHOW INDEX oder beim Zugriff auf die Tabellen TABLES oder STATISTICS von INFORMATION_SCHEMA. (Diese Aktualisierungen ähneln denen für ANALYZE TABLE.) Wenn diese Option deaktiviert ist, aktualisiert InnoDB während dieser Vorgänge keine Statistiken. Das Deaktivieren dieser Variablen kann die Zugriffsgeschwindigkeit für Schemas mit einer großen Anzahl von Tabellen oder Indizes verbessern. Es kann auch die Stabilität von Ausführungsplänen für Abfragen verbessern, an denen InnoDB-Tabellen beteiligt sind.

Wenn Sie es deaktivieren, erhalten Sie möglicherweise eine stabilere Anzahl für das Einrichten von EXPLAIN-Plänen. Dies kann die Leistung SELECT COUNT(*) from mydb.mytableentweder positiv, negativ oder überhaupt nicht beeinflussen. Probieren Sie es aus und sehen Sie !!!

RolandoMySQLDBA
quelle
16

Für den Anfang gibt es keinen "aktuellen Zähler", der in einer Variablen gespeichert werden kann. Eine Abfrage wie diese SELECT COUNT(*) FROM ...unterliegt der aktuellen Isolationsstufe und allen gleichzeitig ausstehenden Transaktionen. Abhängig von der Isolationsstufe kann die Abfrage Zeilen sehen oder nicht sehen, die durch ausstehende nicht festgeschriebene Transaktionen eingefügt oder gelöscht wurden. Die einzige Antwortmöglichkeit besteht darin, die Zeilen zu zählen, die für die aktuelle Transaktion sichtbar sind.

Beachten Sie, dass ich das noch heiklere Thema gleichzeitiger Transaktionen, die während der Zählung beginnen oder enden, nicht einmal angerührt habe . Ganz zu schweigen von Rollbacks ...

Remus Rusanu
quelle
1
Ok, es hängt also von der Isolationsstufe ab, was Sinn macht. Aber es kann immer noch implementiert werden.
Radu Murzea
@SoboLAN Es gibt viele Gründe, warum es nicht sein sollte und warum nicht, von denen die meisten oben aufgeführt sind. Würden Sie es implementieren, indem Sie eine Liste der Zähler pro Tabelle und Transaktionsstart führen (unabhängig davon, welcher Oracle-SCN in MySQL enthalten ist)? Das Verwalten solcher Zählungen wäre ein enormer Aufwand - denken Sie an eine Datenbank mit 100 oder 1000 gleichzeitigen Sitzungen, die jeweils große Mengen von INSERTs / DELETEs in derselben Tabelle ausführen. Unmöglich zu warten.
Philᵀᴹ
Dies umzusetzen ist ziemlich schwierig. Stellen Sie sich vor, die Anzahl muss in der Datenbank gespeichert bleiben, dh irgendwo in den Metadaten, und diese Anzahl muss von jeder Transaktion beibehalten werden, die eine Zeile einfügt oder löscht. Wie würden Sie diese Metadaten sperren ? Und wie würden Sie mit Rollbacks umgehen? Ist alles andere als trivial. Und das Ergebnis wäre für eine sehr sehr enge Teilmenge von Abfragen verwendbar.
Remus Rusanu
3
@ JackDouglas Interessant. Nach dem, was ich in der Vergangenheit gesehen habe, werden COUNT(*)Anfragen in der Realität selten benötigt und sind normalerweise das Ergebnis von Unerfahrenheit der Entwickler (Zählen der Zeilen, bevor wir sie auswählen!) Oder eines schlechten App-Designs.
Philᵀᴹ
1
@SoboLAN - nein, das würde es nicht. Ein Service, der eine Art Statistiktabelle in vordefinierten Zeitintervallen aktualisiert, ist viel besser. Stellen Sie sich vor, Sie haben eine große Datenbank und mehrere Administratoren, die die meisten Tabellen mit abfragen. Fügen Sie der Tabelle SELECT COUNT(*)eine nicht optimierte hinzu WHERE, und Sie haben einige Benutzer, die die Datenbank für einige fragwürdig nützliche Statistikzähler in die Knie zwingen.
NB
0

Während es theoretisch möglich wäre, mit InnoDB die Anzahl der Zeilen für eine bestimmte Tabelle genau zu zählen, würde dies viel Sperren kosten, was sich negativ auf die Leistung auswirken würde. Es würde sich auch aufgrund der Isolationsstufe unterscheiden.

MyISAM führt bereits das Sperren auf Tabellenebene durch, sodass keine zusätzlichen Kosten anfallen.

Ich benötige selten eine Zeilenanzahl für eine Tabelle, obwohl ich COUNT (*) ziemlich oft verwende. Ich habe im Allgemeinen eine WHERE-Klausel angehängt. Wenn ich einen effizienten Index für eine kleine Ergebnismenge verwende, finde ich, dass sie schnell genug sind.

Ich bin nicht einverstanden, dass die Zählungen ungenau sind. Die Zählungen stellen eine Momentaufnahme der Daten dar, und ich habe immer festgestellt, dass sie genau sind.

Kurz gesagt, MySQL überlässt es Ihnen, dies für InnoDB zu implementieren. Sie können eine Zählung speichern und nach jeder Abfrage erhöhen / verringern. Die einfachere Lösung ist wahrscheinlich der Wechsel zu MyISAM.

Marcus Adams
quelle
2
Es ist nicht möglich, die Anzahl der Zeilen in einem Transaktionssystem genau zu zählen. Weil es so viele verschiedene (und korrekte) Zeilenzahlen gibt wie aktive Transaktionen.
a_horse_with_no_name
5
Ich habe hier eine -1 für "Obwohl die einfachere Lösung wahrscheinlich darin besteht, zu MyISAM zu wechseln." Ich würde niemals empfehlen, einfach auf MyISAM zu wechseln, um die Zeilenanzahl zu ermitteln.
Derek Downey
@a_horse_with_no_name, daher stimmen Sie zu, dass es für jede Transaktion eine "korrekte" Zeilenanzahl geben würde. Scheint mir möglich.
Marcus Adams
1
@DTest, ich habe nie gesagt "einfach um die Zeilenzahl zu ermitteln".
Marcus Adams
@a_horse_with_no_name, Das scheint nicht richtig zu sein. Sicherlich zählen wir nur die Anzahl der Zeilen, wenn die Transaktionen festgeschrieben werden, oder?
Pacerier