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 id
Spalte (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 | + ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +
Antworten:
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.
quelle
Wenn Sie die Antwort von @ david-spillett ergänzen, können Sie Ihre Abfrage ändern, indem Sie die Abfrage durch
count(*)
einecount(id)
auf Ihrer Abfrage ersetzen.Weil die
id
Spalte 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 vieleid
s wie Zeilen gibt.Aber selbst wenn Sie ersetzen
count(*)
mitcount(0)
, odercount("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 aEXPLAIN EXTENDED ...
für alle Abfragen vergleichen: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:
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.WHERE
Kriterium hinzu und schränken die zu zählenden Zeilen ein. Dies ist Ihre beste Option.quelle
EXPLAIN EXTENDED select count(*) from table_name; show warnings;
Sie, und Sie werden sehen, dasscount(*)
transformiert wird,count(0)
was äquivalent zucount(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).count(*)
ist an sich keine schlechte Praxis. Aber lesen Sie es noch einmal, wenn Sie meintencount without conditions
(egal ob*
oderid
), dann wäre das eine schlechte :)VARCHAR
Spalten) atable_id int PRIMARY KEY
und 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.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.
quelle
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.
quelle