Ich führe ein EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Die Indizes in meiner Tabelle:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Es gibt einen Index für last_name, der vom Optimierer jedoch nicht verwendet wird.
So ich mache:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Aber nach wie vor ist der Index nicht verwendet! Was mache ich hier falsch?
Hat es damit zu tun, dass der Index ist NON_UNIQUE
? Übrigens ist der NachnameVARCHAR(1000)
Von @RolandoMySQLDBA angefordertes Update
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
2)SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
. Was ist das Ergebnis jeder Zählung?SELECT COUNT(1) FullTableCount FROM employees;
und 2)SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
.Antworten:
PROBLEM 1
Schauen Sie sich die Abfrage an
Ich sehe keine aussagekräftige WHERE-Klausel und der MySQL Query Optimizer auch nicht. Es besteht kein Anreiz, einen Index zu verwenden.
PROBLEM 2
Schauen Sie sich die Abfrage an
Sie gaben ihm einen Index, aber der Query Opitmizer übernahm. Ich habe dieses Verhalten schon einmal gesehen ( Wie erzwinge ich, dass ein JOIN einen bestimmten Index in MySQL verwendet? )
Warum sollte das passieren?
Ohne eine
WHERE
Klausel sagt sich das Abfrageoptimierungsprogramm Folgendes:WHERE
klausel?Das Abfrageoptimierungsprogramm hat den Pfad mit dem geringsten Widerstand gewählt.
Sie werden einen kleinen Schock erleben, aber hier ist er: Wussten Sie, dass das Abfrageoptimierungsprogramm MyISAM ganz anders behandelt?
Sie sagen wahrscheinlich HUH ???? WIE ????
MyISAM speichert die Daten in einer
.MYD
Datei und alle Indizes in der.MYI
Datei.Dieselbe Abfrage erzeugt einen anderen EXPLAIN-Plan, da sich der Index in einer anderen Datei als die Daten befindet. Warum ? Hier ist warum:
last_name
Spalte) sind bereits im bestellt.MYI
last_name
über den Index auf die Spalte zuWie kann man sich da so sicher sein? Ich habe diese Arbeitstheorie dahingehend getestet, wie die Verwendung eines anderen Speichers einen anderen EXPLAIN-Plan (manchmal einen besseren) generiert: Muss ein Index alle ausgewählten Spalten abdecken, damit er für ORDER BY verwendet werden kann?
quelle
Das Problem hierbei ist, dass dies wie ein Präfixindex aussieht. Ich sehe die Tabellendefinition in der Frage nicht, aber
sub_part
= 700? Sie haben nicht die gesamte Spalte indiziert, daher kann der Index nicht zum Sortieren verwendet werden und ist auch nicht als Deckungsindex nützlich. Es konnte nur verwendet werden, um die Zeilen zu finden, die mit einer übereinstimmen könnten,WHERE
und die Serverschicht (über der Speicher-Engine) musste die übereinstimmenden Zeilen weiter filtern. Benötigen Sie wirklich 1000 Zeichen für einen Nachnamen?Update zur Veranschaulichung: Ich habe eine Tabellentesttabelle mit einem Litle von mehr als 500 Zeilen, die jeweils den Domainnamen einer Website in einer Spalte
domain_name VARCHAR(254) NOT NULL
und keine Indizes enthält.Wenn die vollständige Spalte indiziert ist, verwendet die Abfrage den Index:
Jetzt lösche ich diesen Index und indiziere nur die ersten 200 Zeichen von domain_name.
Voila.
Beachten Sie auch, dass der Index mit 200 Zeichen länger ist als der längste Wert in der Spalte ...
... aber das macht keinen Unterschied. Ein mit einer Präfixlänge deklarierter Index kann nur für Nachschlagezwecke verwendet werden, nicht zum Sortieren und nicht als überdeckender Index, da er definitionsgemäß nicht den vollständigen Spaltenwert enthält.
Die obigen Abfragen wurden auch in einer InnoDB-Tabelle ausgeführt, aber die Ausführung in einer MyISAM-Tabelle führt zu praktisch identischen Ergebnissen. Der einzige Unterschied in diesem Fall besteht darin, dass die InnoDB-Zählung
rows
geringfügig abweicht (541), während MyISAM die genaue Anzahl der Zeilen anzeigt (563). Dies ist normal, da die beiden Speicher-Engines Index-Tauchgänge sehr unterschiedlich verarbeiten.Ich würde immer noch behaupten, dass die last_name-Spalte wahrscheinlich größer als nötig ist, aber es ist immer noch möglich , die gesamte Spalte zu indizieren, wenn Sie InnoDB verwenden und MySQL 5.5 oder 5.6 ausführen:
quelle
varchar(1000)
aber dies ist jenseits des für den Index zulässigen Höchstwerts, der bei ~ 750 liegtEXPLAIN SELECT ...
sowieSHOW CREATE TABLE ...
und,SELECT @@VERSION;
da Änderungen am Optimierer möglicherweise versionsübergreifend relevant sind.Ich antwortete, weil ein Kommentar die Formatierung nicht unterstützt und RolandoMySQL DBA über gen_clust_index und innodb sprach. Und das ist sehr wichtig bei einem Innodb-basierten Tisch. Dies geht über die normalen DBA-Kenntnisse hinaus, da Sie in der Lage sein müssen, C-Code zu analysieren.
Sie sollten IMMER EINEN PRIMÄREN SCHLÜSSEL oder EINZIGARTIGEN SCHLÜSSEL erstellen, wenn Sie Innodb verwenden. Wenn Sie dies nicht tun, wird innodb seine eigene generierte ROW_ID verwenden, die Ihnen mehr schaden als nützen könnte.
Ich werde versuchen, es einfach zu erklären, da der Beweis auf C-Code basiert.
Erstes Problem
mutex_enter (& (dict_sys-> mutex));
Diese Zeile stellt sicher, dass nur ein Thread gleichzeitig auf dict_sys-> mutex zugreifen kann. Was wäre, wenn der Wert bereits mutexed wäre? Ja, ein Thread muss warten, damit Sie so etwas wie eine nette zufällige Funktion wie das Sperren von Threads erhalten. Wenn Sie mehr Tabellen ohne Ihren eigenen PRIMARY KEY oder UNIQUE KEY haben, hätten Sie eine nette Funktion mit Innodb ' Table Locking ' ist nicht der Grund, warum MyISAM durch InnoDB ersetzt wurde.
Zweites Problem
(0 == (ID% DICT_HDR_ROW_ID_WRITE_MARGIN))
Modulo (%) -Berechnungen sind langsam und nicht gut, wenn Sie Stapel einfügen, da sie jedes Mal neu berechnet werden müssen ... und weil DICT_HDR_ROW_ID_WRITE_MARGIN (Wert 256) eine Zweierpotenz ist, kann dies viel schneller erfolgen.
(0 == (ID & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1))
Anmerkung: Wenn der C-Compiler für die Optimierung konfiguriert wurde und ein guter Optimierer ist, korrigiert der C-Optimierer den "schweren" Code auf die leichtere Version
Motto der Geschichte Erstellen Sie immer Ihren eigenen PRIMARY KEY oder stellen Sie sicher, dass Sie einen EINZIGARTIGEN Index haben, wenn Sie von Anfang an eine Tabelle erstellen
quelle
UNIQUE
ausreicht - es müssen auch nur Nicht-NULL-Spalten enthalten sein, damit der eindeutige Index in PK hochgestuft wird.INSERT
in dieser Funktion verbracht wird. Ich vermute, ist unbedeutend. Vergleichen Sie den Aufwand, um Spalten herumzuschaufeln, führen Sie BTree-Operationen aus, einschließlich gelegentlichem Block-Split, verschiedenen Mutexen für buffer_pool, Change-Buffer-Stuff usw.