Leistung von MySQL COUNT (*)


7

Ich habe einen Tisch mit mehr als 15m Zeilen. Ich brauche die Gesamtzahl der Zeilen. Damit:

SELECT COUNT(*) FROM thetable;

Das dauert ungefähr 50s. Erklären gibt mir Select tables optimized away. Ich nehme an, dies bedeutet, dass das Ergebnis nur mithilfe eines Index gefunden werden kann. Warum dauert es dann immer noch so lange? Hier sind einige Informationen zum Index in der idSpalte (nicht nullbar):

Indextyp: BTREE (gruppiert)

Kardinalität: 14623100

Einzigartig: JA

Wie kann ich die Leistung dieser Abfrage verbessern? Vielen Dank.

Hinweis: Die Datenbank ist MySQL 5.7.1 und verwendet die InnoDB-Engine.

BEARBEITEN:

Anweisung erstellen:

CREATE TABLE `properties` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `address` varchar(255) DEFAULT NULL,
  `locality` varchar(50) DEFAULT NULL,
  `latitude` decimal(13,9) DEFAULT NULL,
  `longitude` decimal(13,9) DEFAULT NULL,
  `state` varchar(10) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  .....
  PRIMARY KEY (`id`),
  KEY `index_properties_on_address` (`address`),
  KEY `index_properties_on_latitude` (`latitude`),
  KEY `index_properties_on_longitude` (`longitude`),
  KEY `index_properties_on_state` (`state`),
  KEY `index_properties_on_created_at` (`created_at`),
  .....
) ENGINE=InnoDB AUTO_INCREMENT=28267712 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;

Hinweis: Ich habe einige Zeilen weggelassen, es gibt 44 Spalten.

Plan erklären:

+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +
| id | select_type | Tabelle | Partitionen | Typ | mögliche_Tasten | Schlüssel | key_len | ref | Zeilen | gefiltert | Extra |
+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +
| 1 | EINFACH | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Ausgewählte Tabellen auswählen |
+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +

@lunr InnoDB-Zählung ohne Bedingung ist von Natur aus langsamer, da die Zeilen einzeln gezählt und die Transaktionssichtbarkeit für jede Zeile überprüft werden muss.
Jkavalik

Antworten:


6

Damals, als MySQL standardmäßig nicht transaktionssicher war (als Benutzer regelmäßig myISAM-Tabellen anstelle von InnoDB verwendeten, weil dies die Standardeinstellung war, oder, in der Zeit zurück, weil es noch nicht existierte) "SELECT * FROM some_table" ohne Filterklauseln war einer der Abfragetypen, auf die sich die Leute stürzten, weil mySQL in diesen anderen Datenbank-Engines viel schneller war.

In einer transaktionssicheren Umgebung muss das Datenbankmodul im Allgemeinen jede Zeile überprüfen und sicherstellen, dass sie für die aktuelle Sitzung sichtbar ist (dh, sie ist nicht Teil einer Transaktion, die noch nicht festgeschrieben wurde (oder bei der sie nicht festgeschrieben wurde) Der Beginn dieser Sitzung (aktive Transaktion) oder wird gerade zurückgesetzt. Wenn Sie jede Zeile überprüfen, müssen Sie einen Tabellenscan oder (sofern vorhanden) einen Clustered-Index-Scan durchführen.

Es wäre für die Engine möglich , die Anzahl der in jedem Objekt sichtbaren Zeilen für jede aktive Sitzung / Transaktion zu verfolgen, aber vermutlich haben die Designer dies nicht als die zusätzliche Verarbeitung wert eingeschätzt, sodass ich davon ausgehe, dass dies im Allgemeinen nicht als praktisch angesehen wird- Ich kann mir vorstellen, dass es einige ziemlich komplexe Sperranforderungen für den Umgang mit Parallelität geben würde, die die Leistung anderer Vorgänge zu sehr beeinträchtigen würden. Sie können dies selbst implementieren, indem Sie eine Tabelle führen, in der die Anzahl der Zeilen in der interessierenden Tabelle aufgezeichnet ist, und Ihren gesamten Code diesen Wert sorgfältig beibehalten. Dies wäre jedoch ein ziemlicher Aufwand und kann aufgrund von Fehlern übermäßig anfällig sein Fehler, die bedeuten, dass die Anzahl im Laufe der Zeit von true abweicht (und Sie wahrscheinlich eine potenzielle Deadlock-Quelle und / oder einen Sperrengpass auf der Anwendungsebene hinzufügen).

Situationen, in denen Sicherheit auf Zeilenebene verwendet wird, erschweren dies noch mehr. Sie müssen nicht nur den Status einer Zeile / Seite in Bezug auf die aktuelle Transaktion überprüfen, sondern auch den aktuellen Benutzer und die Sicherheitsregeln erneut überprüfen Dynamisch wäre es unpraktisch, diese Informationen zwischenzuspeichern, was den Scan jedes Mal für alle Fälle erforderlich macht. MS SQL Server wird in der nächsten Version ( https://msdn.microsoft.com/en-us/library/dn765131.aspx ) Sicherheit auf Zeilenebene hinzugefügt und ist bereits in postgres ( http: //www.postgresql) vorhanden .org / docs / 9.5 / static / ddl-rowsecurity.html ), ich weiß nichts über den Status in anderen RDBMS.


3

Wenn Sie die Antwort von @ david-spillett ergänzen, können Sie Ihre Abfrage ändern, indem Sie die Abfrage durch count(*)eine count(id)auf Ihrer Abfrage ersetzen.

SELECT COUNT(id) FROM thetable;

Weil die idSpalte nicht null ist, indiziert (eigentlich ist es der Primärschlüssel), was bedeutet, dass sie nicht für alle Zeilen null ist und es daher so viele ids wie Zeilen gibt.

Aber selbst wenn Sie ersetzen count(*)mit count(0), oder count("Hi, I'm a row")Sie würden die gleiche Leistung haben, weil intern führen sie im selben Betrieb. Sie können es überprüfen, indem Sie das Ergebnis von a EXPLAIN EXTENDED ...für alle Abfragen vergleichen:

EXPLAIN EXTENDED SELECT COUNT(*) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(id) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(0) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT("Hi, I'm a row") FROM thetable;

Derzeit ist InnoDB select count(<whatever>) from table_name ;ohne jegliche Bedingungen nicht die beste Vorgehensweise.

Diese Art der Abfrage bietet eine bessere Leistung, wenn:

  1. Ihr kleinster Index in der Tabelle befindet sich in einer sehr kleinen Spalte (z. B. einem winzigen Index) anstelle eines zusammengesetzten Index oder in einer großen Spalte (wie z. B. a). Fügen Sie ihn varchar(200)jedoch nicht hinzu, um diese Art der Auswahl zu verbessern. Dies liegt daran, dass InnoDB mit einem kleineren Index weniger Daten zum Scannen hat.
  2. Sie fügen ein WHEREKriterium hinzu und schränken die zu zählenden Zeilen ein. Dies ist Ihre beste Option.

3
Überprüfen EXPLAIN EXTENDED select count(*) from table_name; show warnings;Sie, und Sie werden sehen, dass count(*)transformiert wird, count(0)was äquivalent zu count(PK)(oder einem anderen Nicht-Null-Index) ist. Tatsächlich kann für diese Abfrage jeder sekundäre InnoDB-Index verwendet werden (da alle die PK-Werte enthalten), und er ist normalerweise schneller als die direkte Verwendung der PK (weniger zu lesende Daten).
Jkavalik

Jeden Tag lernen. Aber was ist konkret falsch an meiner Antwort?
Nuno Pereira

1
Nun, die Modifikation soll nichts verbessern und imho count(*)ist an sich keine schlechte Praxis. Aber lesen Sie es noch einmal, wenn Sie meinten count without conditions(egal ob *oder id), dann wäre das eine schlechte :)
jkavalik

Vielen Dank für Ihre Kommentare. Ich habe meine Antwort verbessert oder zumindest versucht, angesichts Ihrer Eingabe.
Nuno Pereira

2
Eine andere Sache, die in InnoDB sehr einfach zu testen ist. Erstellen Sie eine breite Tabelle (z. B. mit einigen VARCHARSpalten) a table_id int PRIMARY KEYund fügen Sie dann einen weiteren Index hinzu (table_id). Die Zählabfragen würden diesen Index anstelle des PK-Index verwenden - da dies per Definition der engste Index ist, den Sie in einer InnoDB-Tabelle haben können.
Ypercubeᵀᴹ

1

Erstellen Sie eine neue Tabelle (properties_count (id, count)) und verwenden Sie den Trigger zum Einfügen (Inkrementanzahl) und zum Löschen (Dekrementzähler).

Danach können Sie Folgendes verwenden: Wählen Sie count aus properties_count aus.


0

Wenn Sie diese Abfrage profilieren könnten, hätten wir möglicherweise weitere Informationen zu diesem Problem. Eines ist sicher: Da die Speicher-Engine InnoDB ist, haben die Innodb-Puffer einen Einfluss.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.