MySQL-Indizierung VarChar


10

Ich versuche, meine blogentriesDatenbank für eine bessere Leistung zu indizieren, habe jedoch ein Problem festgestellt.

Hier ist die Struktur:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Eine Abfrage wie die folgende verwendet den Index ordnungsgemäß:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | Tabelle | Typ | mögliche_Tasten | Schlüssel | key_len | ref | Zeilen | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | EINFACH | blogentries | Index | NULL | PRIMARY | 114 | NULL | 126 | Index verwenden |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Wenn ich das entry_idin die SELECTAbfrage einfüge, wird jedoch der Dateisort verwendet

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | Tabelle | Typ | mögliche_Tasten | Schlüssel | key_len | ref | Zeilen | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | EINFACH | blogentries | ALL | NULL | NULL | NULL | NULL | 126 | Dateisortierung verwenden |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Ich habe mich gefragt, warum das passiert und wie ich es vermeiden kann. Liegt es an dem VarChar, und das sollte in etwas anderes geändert werden?

Ich versuche, dass alle meine Abfragen den Index verwenden, da ich auf hohe Werte Handler_read_rndund Handler_read_rnd_nextWerte stoße.

Wenn Sie weitere Informationen benötigen, kann ich diese auch posten.


Dateisortierung bedeutet, dass die Sortierung auf der Festplatte durchgeführt wird.
Kermit

Versuchen Sie WHERE 1=1, Ihrer zweiten Abfrage etwas hinzuzufügen .
Kermit

Welche Version von MySQL ist das? Was ist Ihre Sortierpuffergröße ( SELECT @@sort_buffer_size)?

@njk filesort ist ein Ergebnis des Teils 'ORDER BY' der Abfrage

1
@TashPemhiwa Nicht unbedingt, siehe die erste Aussage.
Kermit

Antworten:


6

Da Sie WHEREin keiner der Abfragen eine Klausel haben , geben Sie in beiden Fällen alle Zeilen zurück. Daher würde die Verwendung oder Nichtverwendung des Index in diesen Beispielen nur sehr geringe Auswirkungen auf die Leistung haben.


Sicherlich sollte MySQL den Index für das ORDER BY?
Eggyal

@eggyal Nicht, wenn es zu groß für Speicher ist.
Kermit

@njk: Das macht keinen Sinn ... es kann den Index der Reihe nach durchlaufen, ohne dass das Ganze in den Speicher geladen werden muss. Die Ergebnisse werden sortiert, ohne dass eine Dateisortierung durchgeführt werden muss.
Eggyal

@eggyal Ich würde die Größe von in Frage stellen varchar(5000).
Kermit

@njk: Aber diese Spalte befindet sich weder im Index noch wird sie in der Sortierung verwendet.
Eggyal

2

Wie unter ORDER BYOptimierung dokumentiert :

filesortVersuchen Sie bei langsamen Abfragen, für die nicht verwendet wird, max_length_for_sort_dataauf einen Wert zu senken , der zum Auslösen von a geeignet ist filesort.

In seinem Blog-Artikel Was genau ist read_rnd_buffer_size , erklärt Peter Zaitsev:

Für mich bedeutet dies, dass diese Option seit MySQL 4.1 in engen Fällen verwendet wird. Wenn Sie nur wenige Felder abrufen (weniger als max_length_for_sort_data ), sollten Daten im Sortierpuffer und in der Sortierdatei gespeichert werden, sodass read_rnd_buffer nicht erforderlich ist, wenn die ausgewählten Spalten sind lang, daher sind sie länger als max_length_for_sort_data. Dies würde häufig bedeuten, dass sich einige TEXT / BLOB-Spalten unter ihnen befinden. Es wird jedoch verwendet, wenn eine große Anzahl von Spalten vorhanden ist oder lange VARCHAR-Spalten verwendet werden. Es sind nur einige UTF8-VARCHAR (255) erforderlich , um eine Zeile zu erstellen, die in ihrer statischen Darstellung länger als max_length_for_sort_data ist .

Dies deutet darauf hin, dass max_length_for_sort_datadie Gesamtgröße der ausgewählten Spalten begrenzt ist, oberhalb derer a filesortanstelle einer indexbasierten Sortierung verwendet wird.

In Ihrem Fall nimmt die Auswahl entry_id(5002 Byte) die Gesamtgröße über den 1-KB-Standardwert dieser Variablen und wird daher filesortverwendet. Um das Limit auf 8 KB zu erhöhen, können Sie Folgendes tun:

SET SESSION max_length_for_sort_data = 8192;

Ich habe eine Tabelle mit einem sehr ähnlichen Setup wie diese, und diese Einstellung scheint keine Änderungen bei der Verwendung von Dateisortierung auszulösen.

@muffinista: Das ist interessant. Ich nehme an, es könnte mit einigen der anderen Puffereinstellungen zusammenhängen, gemäß der Antwort von @ RolandoMySQLDBA ?
Eggyal

2

Sie haben hier viele interessante Antworten erhalten, aber niemand hat die Frage genau beantwortet - warum passiert das? Wenn eine SELECT-Abfrage Daten mit variabler Länge in MySQL enthält und es keinen Index gibt, der mit ALLEN angeforderten Spalten übereinstimmt, wird nach meinem Verständnis immer ein Dateisort verwendet. Die Größe der Daten ist hier nicht besonders relevant. Es ist schwer, eine direkte Antwort auf diese Frage in der MySQL-Dokumentation zu finden, aber hier ist ein guter Blog-Beitrag, in dem jemand ein sehr ähnliches Problem wie Sie hat.

Siehe auch: 10 Tipps zur Optimierung von MySQL-Abfragen (die nicht scheißen) .

Wenn es also sinnvoll ist, einen Index für entry_id zu haben, können Sie ihn hinzufügen und fertig sein. Aber ich bezweifle, dass es eine Option ist. Was tun?

Ob Sie etwas dagegen unternehmen sollten, ist eine separate Frage. Es ist wichtig zu wissen, dass 'filesort' in MySQL schlecht benannt ist - es ist wirklich nur der Name des Algorithmus, der zum Sortieren dieser bestimmten Abfrage verwendet wird, und in vielen Fällen erfolgt die Sortierung tatsächlich im Speicher. Wenn Sie nicht erwarten, dass dieser Tisch stark wächst, ist dies wahrscheinlich keine große Sache.

Wenn diese Tabelle jedoch eine Million Zeilen enthält, liegt möglicherweise ein Problem vor. Wenn Sie die Paginierung von Abfragen in dieser Tabelle unterstützen müssen, liegt hier möglicherweise ein wirklich ernstes Leistungsproblem vor. In diesem Fall ist es eine gültige Optimierung, Ihre Daten variabler Länge in eine neue Tabelle zu partitionieren und einen JOIN durchzuführen, um sie abzurufen.

Hier sind ein paar andere Antworten auf SO, die sich mit dieser Frage befassen:


Die erste Abfrage des OP " enthält Daten variabler Länge in MySQL und es gibt keinen Index, der mit ALLEN angeforderten Spalten übereinstimmt ", filesortwurde jedoch in diesem Fall anscheinend nicht verwendet. Ich denke auch, dass sich das Sortieren einer kleinen Tabelle im Speicher allein als inakzeptabler Leistungseinbruch erweisen könnte: z. B. wenn die Abfrage häufig ausgeführt wird (und die Tabelle sich ändert, sodass Caches nicht verwendet werden können).
Eggyal

Ich habe keine Zeit, es zu testen, aber ich frage mich, ob dies durch ein VARCHAR ausgelöst wird, das 2 Bytes zum Speichern der in dev.mysql.com/doc/refman/5.1/en/char angegebenen Länge benötigt. html - die erste Abfrage passt also in diese Grenze, die zweite jedoch nicht.

0

Fügen Sie WHEREIhren Abfragen eine Klausel hinzu .

Der Index kann auch dann verwendet werden, wenn ORDER BY nicht genau mit dem Index übereinstimmt, solange alle nicht verwendeten Teile des Index und alle zusätzlichen ORDER BY- Spalten Konstanten in der WHERE- Klausel sind. In einigen Fällen kann MySQL keine Indizes verwenden, um ORDER BY aufzulösen , obwohl es weiterhin Indizes verwendet, um die Zeilen zu finden, die mit der WHERE- Klausel übereinstimmen .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


In diesem Fall ORDER BY stimmt das jedoch genau mit dem Index überein, sodass keine WHEREKlausel erforderlich ist.
Eggyal

Ich habe eine "where" -Klausel in der eigentlichen Abfrage auf der Site, daher weiß ich, dass dies nicht die Ursache für die Dateisortierung ist. Ich frage mich, ob es die Verwendung von Varchar ist?

0

Nach meinem Kenntnisstand kann varchar nur maximal 8000 Bytes aufnehmen, was ungefähr 4000 Zeichen entspricht. Somit scheinen 5000 die Speichergrenze zu überschreiten, und in diesem Fall wahrscheinlich der Grund, warum die Sortierung durcheinander gebracht wird.

"varchar [(n | max)] Nicht-Unicode-Zeichendaten variabler Länge. n kann ein Wert zwischen 1 und 8.000 sein. max gibt an, dass die maximale Speichergröße 2 ^ 31-1 Byte beträgt. Die Speichergröße ist die tatsächliche Länge der eingegebenen Daten + 2 Bytes. Die eingegebenen Daten können 0 Zeichen lang sein. Die SQL-2003-Synonyme für varchar sind Zeichen oder Zeichen. "

Hoffe das beantwortet deine Frage


Wie unter The CHARund VARCHARTypes dokumentiert : " Werte in VARCHAR-Spalten sind Zeichenfolgen variabler Länge. Die Länge kann als Wert von 0 bis 255 vor MySQL 5.0.3 und von 0 bis 65.535 in 5.0.3 und späteren Versionen angegeben werden Die maximale Länge von a VARCHARin MySQL 5.0.3 und höher hängt von der maximalen Zeilengröße (65.535 Byte, die von allen Spalten gemeinsam genutzt wird) und dem verwendeten Zeichensatz ab. "
eggyal

0

Sie haben nur 126 Zeilen in Ihrer Tabelle. Selbst wenn jede Zeile maximal 5 KB groß ist, bedeutet dies, dass die Gesamtgröße zum Lesen von der Festplatte nur etwa 600 KB beträgt - dies ist keine ganze Menge. Um ehrlich zu sein, es ist eine sehr kleine Menge, wahrscheinlich weniger als die Cache-Größe der meisten modernen Festplatten.

Wenn der Server Ihre Daten abrufen muss, um Ihre Anfrage zu erfüllen, ist es am teuersten, sie von der Festplatte zu lesen. Das Lesen gemäß der Indexreihenfolge ist jedoch NICHT immer der schnellste Weg, insbesondere wenn die Datenmenge so gering ist.

In Ihrem Fall ist es VIEL effizienter, ganze Tabellendaten als einzelnen Block von der Festplatte in den Speicher zu lesen (wahrscheinlich in nur einer Leseoperation oder Suche) und dann im RAM zu sortieren, um ORDER BY zu erfüllen, was im Vergleich zur Festplatte sofort erfolgt Lesevorgang. Wenn der Server Ihre Daten gemäß dem Index liest, muss er bis zu 126 (oops!) Lesevorgänge ausführen und viele Male in derselben Datendatei hin und her suchen.

Mit anderen Worten, sequentieller Scan ist NICHT immer eine schlechte Sache, und MySQL ist nicht unbedingt dumm. Wenn Sie versuchen, MySQL zu zwingen, diesen Index zu verwenden, funktioniert er höchstwahrscheinlich langsamer als der derzeitige sequentielle Scan.

Der Grund, warum der Index verwendet wurde, wenn kein 5-KB-Feld enthalten war, liegt darin, dass die dann abgerufenen Daten nicht 99% der Daten in der Tabelle ausmachten. Wenn Sie Ihr 5-KB-Feld eingefügt haben, muss die Abfrage jetzt 99% der Daten lesen, und es ist billiger, das Ganze zu lesen und es anschließend im Speicher zu sortieren.


Es hört sich so an, als würden Sie eine Reihe von Dingen aus dem Vermeiden vollständiger Tabellenscans durcheinander bringen , die mit der Indexverwendung in zufriedenstellenden JOINBedingungen und WHEREKlauseln zu tun haben , nicht mit ORDER BYKlauseln.
Eggyal

Genau das Gegenteil. In diesem speziellen Fall ist ein vollständiger Tabellenscan eine gute Sache, einfach weil er SCHNELLER ist als das Lesen nach Indexreihenfolge.

0

Welche Version von MySQL verwenden Sie?

In 5.1 habe ich versucht, Ihr Szenario einzurichten, und einige Dummy-Daten ausgefüllt. Mit den von Ihnen bereitgestellten SQLs erhalte ich jedes Mal nur einen Tabellenscan gemäß EXPLAIN. Standardmäßig wird bei Verwendung von order by MYSQL auf filesort zurückgegriffen, auch wenn der Primärindex in der Reihenfolge by verwendet wird.

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.