it-swarm.com.de

Leistung von MySQL COUNT (*)

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 Spalte id (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.

EDIT :

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 | 
 + ---- + ------------- + ------- + ------------ + ------ + --------------- + ------ + --------- + ------ + - ---- + ---------- + -------------------------- + 
7
lunr

Damals, als MySQL standardmäßig nicht transaktionssicher war (wenn 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 möglich , dass die Engine die Anzahl der in jedem Objekt sichtbaren Zeilen für jede aktive Sitzung/Transaktion verfolgt, aber vermutlich haben die Designer dies nicht getan Ich bin der Meinung, dass dies die zusätzliche Verarbeitung wert ist, und gehe daher davon aus, dass dies nicht allgemein als praktikabel angesehen wird . Ich kann mir vorstellen, dass einige recht komplexe Sperranforderungen zu bewältigen sind mit Parallelität, die die Leistung anderer Operationen zu sehr beeinträchtigen würde. 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 nur für den Fall erforderlich macht. Die Sicherheit auf Zeilenebene wird in der nächsten Version zu MS SQL Server hinzugefügt ( https://msdn.Microsoft.com/en-us/library/dn765131.aspx ) und ist bereits in postgres ( http://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.html ) Ich weiß nichts über den Status in anderen RDBMS.

6
David Spillett

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

SELECT COUNT(id) FROM thetable;

Weil die Spalte id 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 gibt, wie es gibt Reihen.

Aber selbst wenn Sie count(*) durch count(0) oder count("Hi, I'm a row") ersetzen, haben Sie dieselbe Leistung, da sie intern zu derselben Operation führen. Sie können es überprüfen, indem Sie das Ergebnis eines EXPLAIN EXTENDED ... Bei allen 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 select count(<whatever>) from table_name ; für InnoDB ohne 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 einer varchar(200)). Fügen Sie ihn jedoch nicht nur zur Verbesserung hinzu diese Art der Auswahl. Dies liegt daran, dass InnoDB mit einem kleineren Index weniger Daten scannen muss.
  2. Sie fügen ein WHERE -Kriterium hinzu und schränken die zu zählenden Zeilen ein. Dies ist Ihre beste Option.
3
Nuno Pereira

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.

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.

0