MySQL-Indizierung meistern für schnellere Abfrageleistung
Entfesseln Sie schnellere Datenbankleistung mit unserem umfassenden Leitfaden zur MySQL-Indizierung. Erfahren Sie mehr über wesentliche Indextypen (PRIMARY KEY, UNIQUE, INDEX, FULLTEXT), bewährte Methoden zum Erstellen und Verwalten von zusammengesetzten Indizes und wie Sie die Indexnutzung mit der leistungsstarken EXPLAIN-Anweisung analysieren. Optimieren Sie Ihre Abfragen und beschleunigen Sie den Datenabruf erheblich für eine effizientere MySQL-Datenbank.
MySQL-Indizierung meistern für schnellere Abfrageleistung
Die MySQL-Indizierung ist normalerweise der erste Ort, an den ich mich wende, wenn eine Datenbank langsam erscheint, aber es ist auch einer der einfachsten Orte, um einen selbstbewussten Fehler zu machen. Ein Index kann einen Tabellenscan in eine schnelle Suche verwandeln. Er kann auch Schreibvorgänge verlangsamen, Speicher verschwenden und ein falsches Gefühl des Fortschritts vermitteln, wenn der Abfrageplaner ihn nie verwendet.
Die praktische Frage ist nicht "Sollte diese Spalte einen Index haben?" Die bessere Frage ist: "Welche Abfrage versuche ich billiger zu machen, und wie werde ich beweisen, dass der Index geholfen hat?" Behalten Sie diese Frage beim Lesen im Hinterkopf. Gute Indizierung beginnt mit echten Abfragen, nicht mit einer Liste von Spalten, die wichtig aussehen.
Was ist ein MySQL-Index?
Ein MySQL-Index ist eine Datenstruktur, die die Geschwindigkeit von Datenabrufoperationen in einer Datenbanktabelle verbessert. Stellen Sie es sich wie einen Index in einem Buch vor: Anstatt das gesamte Buch zu lesen, um ein bestimmtes Thema zu finden, können Sie das Thema im Index nachschlagen, der Ihnen die genaue Seitenzahl verrät. Ähnlich ermöglicht ein Datenbankindex MySQL, Zeilen, die einer bestimmten Abfragebedingung entsprechen, schnell zu lokalisieren, ohne die gesamte Tabelle zu scannen.
Wenn Sie eine Tabelle abfragen, kann MySQL einen Index verwenden, um die relevanten Zeilen viel schneller zu finden, als wenn es jede Zeile untersuchen würde. Dies ist besonders vorteilhaft für Tabellen mit einer großen Anzahl von Zeilen oder für Abfragen, die Filterung (WHERE-Klauseln), das Verknüpfen von Tabellen (JOIN-Klauseln) oder Sortierung (ORDER BY-Klauseln) beinhalten.
Wie Indizes funktionieren
MySQL verwendet üblicherweise B-Baum-Indizes für normale InnoDB-Indizes. Ein B-Baum hält Schlüssel in sortierter Reihenfolge, weshalb er gut für Gleichheitssuchen, Bereiche, geordnete Scans und viele Joins geeignet ist. Wenn Sie einen Index für eine oder mehrere Spalten erstellen, baut MySQL eine Struktur auf, in der:
- Blattknoten die eigentlichen Datenzeiger oder, im Falle von geclusterten Indizes (wie InnoDBs Primärschlüssel), die Datenzeilen selbst enthalten.
- Interne Knoten Schlüssel enthalten, die helfen, den Baum zu navigieren, um den richtigen Blattknoten zu finden.
Wenn eine Abfrage die linke Seite dieses Index verwenden kann, kann MySQL zu einem schmalen Teil des Baums navigieren, anstatt die gesamte Tabelle zu lesen. Das ist der eigentliche Gewinn. Der Index macht nicht jede Abfrage schnell; er macht bestimmte Zugriffsmuster billig.
Arten von MySQL-Indizes
MySQL unterstützt verschiedene Arten von Indizes, jede mit ihren eigenen Stärken und Anwendungsfällen.
1. PRIMARY KEY
- Eine
PRIMARY KEY-Einschränkung stellt sicher, dass jeder Wert in der(n) Spalte(n) eindeutig und nichtNULList. Sie wird implizit indiziert. - Eine Tabelle kann nur einen
PRIMARY KEYhaben. - InnoDB-Tabellen sind physisch nach ihrem Primärschlüssel (geclusterter Index) geordnet.
Beispiel:
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100)
);
2. UNIQUE Index
- Ein
UNIQUE-Index erzwingt, dass alle Werte in der/den indizierten Spalte(n) unterschiedlich sind. Er erlaubtNULL-Werte, aber mehrereNULLs sind erlaubt (es sei denn, die Spalte ist auch Teil einesPRIMARY KEYoder einer anderenUNIQUE-Einschränkung, die dies verhindert). - Nützlich zur Sicherstellung der Datenintegrität, wenn eine Spalte eindeutig sein muss, aber nicht der primäre Bezeichner ist.
Beispiel:
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
sku VARCHAR(50) UNIQUE
);
3. INDEX (oder KEY)
- Ein Standardindex, auch als nicht-eindeutiger Index bezeichnet.
- Wird verwendet, um den Datenabruf zu beschleunigen. Erzwingt keine Eindeutigkeit.
Beispiel:
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
INDEX idx_customer_id (customer_id)
);
4. FULLTEXT Index
- Wird für Volltextsuchen in
CHAR-,VARCHAR- undTEXT-Spalten verwendet. - Ermöglicht Stichwortsuchen in großen Textfeldern.
- Wird von InnoDB in modernen MySQL-Versionen unterstützt. Ältere MySQL-Installationen können andere Einschränkungen haben, überprüfen Sie daher Ihre genaue Version, bevor Sie darauf aufbauen.
Beispiel:
CREATE TABLE articles (
article_id INT PRIMARY KEY,
title VARCHAR(255),
body TEXT,
FULLTEXT (title, body)
);
5. SPATIAL Index
- Wird zum Indizieren räumlicher Datentypen wie Punkte, Linien und Polygone verwendet.
- Verhalten und Anforderungen variieren je nach MySQL-Version und Speicher-Engine. Testen Sie die genaue räumliche Abfrage, die Sie ausführen möchten; gehen Sie nicht davon aus, dass ein räumlicher Index jedem GIS-ähnlichen Prädikat hilft.
6. HASH Index (Begrenzte Nutzung)
- Die
MEMORY-Speicher-Engine von MySQL unterstütztHASH-Indizes. Sie sind für Gleichheitssuchen ausgelegt, nicht für Bereichsscans oder Sortierungen. - Kein Allzweck-Indextyp für die meisten gängigen Szenarien.
Erstellen und Verwalten von Indizes
So erstellen Sie einen Index
Sie können Indizes entweder beim Erstellen einer Tabelle oder durch Ändern einer vorhandenen Tabelle erstellen.
1. Bei der Tabellenerstellung:
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
department_id INT,
hire_date DATE,
INDEX idx_department (department_id),
INDEX idx_hire_date (hire_date)
);
2. Ändern einer vorhandenen Tabelle:
-- Einen einspaltigen Index hinzufügen
ALTER TABLE customers
ADD INDEX idx_email (email);
-- Einen eindeutigen Index hinzufügen
ALTER TABLE users
ADD UNIQUE INDEX uidx_username (username);
-- Einen mehrspaltigen (zusammengesetzten) Index hinzufügen
ALTER TABLE orders
ADD INDEX idx_customer_date (customer_id, order_date);
So löschen Sie einen Index
Wenn ein Index nicht mehr benötigt wird oder die Leistung negativ beeinflusst (z. B. bei Schreibvorgängen), können Sie ihn löschen.
-- Einen Standardindex löschen
ALTER TABLE customers
DROP INDEX idx_email;
-- Einen eindeutigen Index löschen
ALTER TABLE users
DROP INDEX uidx_username;
Mehrspaltige (zusammengesetzte) Indizes
Zusammengesetzte Indizes werden für zwei oder mehr Spalten erstellt. Die Reihenfolge der Spalten in einem zusammengesetzten Index ist entscheidend.
- Ein zusammengesetzter Index auf
(col1, col2)kann für Abfragen verwendet werden, die nur nachcol1filtern, oder sowohl nachcol1UNDcol2. - Er wird im Allgemeinen nicht für Abfragen verwendet, die nur nach
col2filtern.
Beispiel:
Betrachten Sie einen Index auf (customer_id, order_date). Dieser Index ist am effektivsten für Abfragen wie:
SELECT * FROM orders WHERE customer_id = 123;
SELECT * FROM orders WHERE customer_id = 123 AND order_date = '2023-10-27';
SELECT * FROM orders WHERE customer_id = 123 ORDER BY order_date;
Er könnte für SELECT * FROM orders WHERE order_date = '2023-10-27'; nicht sehr nützlich sein.
Bewährte Methoden für die MySQL-Indizierung
1. Indizieren Sie Spalten, die in WHERE-, JOIN- und ORDER BY-Klauseln verwendet werden
Dies sind die häufigsten Stellen, an denen Indizes erhebliche Leistungsvorteile bieten.
WHERE-Klauseln: Filterbedingungen sind der primäre Anwendungsfall.JOIN-Bedingungen: Das Indizieren von Spalten, die inON-Klauseln vonJOIN-Anweisungen verwendet werden, beschleunigt Tabellenverknüpfungen erheblich.ORDER BY- undGROUP BY-Klauseln: Indizes können MySQL helfen, Sortiervorgänge zu vermeiden.
2. Verwenden Sie zusammengesetzte Indizes mit Bedacht
- Die Reihenfolge ist wichtig: Setzen Sie die Spalten, die zur Abfrageform passen, an die erste Stelle. Gleichheitsfilter kommen normalerweise vor Bereichsfiltern. Spalten, die für die Sortierung verwendet werden, können nach den Filterspalten helfen.
- Setzen Sie nicht blind die selektivste Spalte an die erste Stelle, wenn Ihre tatsächlichen Abfragen nicht danach filtern. Ein Index auf
(status, created_at)kann hervorragend fürWHERE status = 'paid' ORDER BY created_at DESC LIMIT 50sein, selbst wennstatuseine niedrige Kardinalität hat, weil er dem Zugriffsmuster entspricht.
3. Verwenden Sie EXPLAIN vorher und nachher
Beurteilen Sie einen Index niemals nur aufgrund von Hoffnung. Führen Sie EXPLAIN aus, fügen Sie den Index in einer Staging- oder wartungssicheren Umgebung hinzu und führen Sie dann erneut EXPLAIN aus.
EXPLAIN
SELECT order_id, total, created_at
FROM orders
WHERE customer_id = 123
AND created_at >= '2025-01-01'
ORDER BY created_at DESC
LIMIT 20;
Achten Sie auf type, key, rows und Extra. Wenn key NULL ist, hat MySQL keinen Index ausgewählt. Wenn rows immer noch nahe an der Tabellengröße liegt, ist der Index für diese Abfrage möglicherweise nicht selektiv genug. Wenn Extra Using filesort sagt, ist das nicht automatisch schlecht, aber es sagt Ihnen, dass MySQL die Zeilen nicht in der angeforderten Reihenfolge aus dem gewählten Index zurückgeben konnte.
Für MySQL 8.0.18 und höher kann EXPLAIN ANALYZE noch nützlicher sein, da es die Abfrage ausführt und die tatsächliche Zeit und Zeilenanzahlen meldet:
EXPLAIN ANALYZE
SELECT order_id, total
FROM orders
WHERE customer_id = 123
ORDER BY created_at DESC
LIMIT 20;
Verwenden Sie es vorsichtig in Produktionssystemen, da es die Anweisung ausführt.
4. Bauen Sie Indizes um Workflows herum, nicht um Tabellen
Stellen Sie sich einen Admin-Bildschirm vor, der kürzlich fehlgeschlagene Zahlungen auflistet:
SELECT id, customer_id, failure_code, created_at
FROM payments
WHERE status = 'failed'
ORDER BY created_at DESC
LIMIT 100;
Ein Index nur auf status kann MySQL immer noch dazu zwingen, eine große Anzahl fehlgeschlagener Zeilen zu sortieren. Ein Index auf (status, created_at) ist normalerweise eine bessere Übereinstimmung, da MySQL fehlgeschlagene Zeilen finden und sie in zeitlicher Reihenfolge lesen kann. Wenn die Abfrage nur Spalten im Index zurückgibt, könnten Sie einen abdeckenden Index in Betracht ziehen:
CREATE INDEX idx_payments_status_created_cover
ON payments (status, created_at, id, customer_id, failure_code);
Das kann schnell sein, ist aber nicht kostenlos. Der Index ist breiter, benötigt mehr Speicher und kostet bei jedem Schreibvorgang mehr. Ich greife nur bei heißen Abfragen zu abdeckenden Indizes, die oft genug ausgeführt werden, um die zusätzlichen Wartungskosten zu rechtfertigen.
5. Achten Sie auf Indizes, die richtig aussehen, aber nichts tun
Dies sind häufige Fallstricke:
- Eine Funktion versteckt den indizierten Wert:
WHERE DATE(created_at) = '2025-01-01'. - Ein führender Platzhalter verhindert die normale B-Baum-Nutzung:
WHERE email LIKE '%@example.com'. - Ein Typkonflikt erzwingt eine Konvertierung: Der Vergleich einer Integer-Spalte mit einer in Anführungszeichen gesetzten Zeichenfolge kann funktionieren, aber er kann Pläne in realen Schemata verwirren.
- Der Index beginnt mit der falschen Spalte für die Abfrage:
(created_at, customer_id)ist nicht dasselbe wie(customer_id, created_at).
Schreiben Sie das Prädikat um, wenn Sie können:
WHERE created_at >= '2025-01-01'
AND created_at < '2025-01-02'
Diese Form ermöglicht es MySQL, einen Bereichsscan auf created_at zu verwenden.
6. Entfernen Sie redundante und ungenutzte Indizes sorgfältig
Übermäßige Indizierung ist ein leises Leistungsproblem. Jeder zusätzliche Sekundärindex muss während INSERT, UPDATE und DELETE gewartet werden. Bei einer schreibintensiven Tabelle können fünf ungenutzte Indizes mehr ausmachen als ein langsames SELECT.
In MySQL 5.7 und 8.0 kann das sys-Schema Ihnen helfen, Kandidaten zu finden:
SELECT *
FROM sys.schema_unused_indexes
WHERE object_schema = 'app';
Behandeln Sie diese Ausgabe als Hinweis, nicht als Befehl. Ein Index kann ungenutzt erscheinen, weil der Server kürzlich neu gestartet wurde, weil ein monatlicher Bericht noch nicht ausgeführt wurde oder weil der Datenverkehr in der Staging-Umgebung nicht mit der Produktion übereinstimmt. Bevor Sie einen Index löschen, überprüfen Sie den Bereitstellungsverlauf, geplante Jobs und Fremdschlüsselanforderungen.
7. Fügen Sie Indizes sicher auf großen Tabellen hinzu
Bei kleinen Tabellen ist ALTER TABLE ... ADD INDEX normalerweise ereignislos. Bei einer großen Produktionstabelle kann es eine echte Operation sein. Abhängig von der MySQL-Version, der Speicher-Engine, der Tabellendefinition und dem genauen DDL kann das Hinzufügen eines Index Online-DDL verwenden oder immer noch Druck durch Metadatensperren, temporären Speicherplatz, Redo-Generierung und Replikationsverzögerung erzeugen.
Bevor Sie einen großen Index hinzufügen, überprüfen Sie:
- Wie groß die Tabelle und die vorhandenen Indizes sind.
- Ob Replikate mithalten können.
- Ob Ihre MySQL-Version den Online-Algorithmus unterstützt, den Sie erwarten.
- Ob die Anwendung eine Metadatensperre tolerieren kann, wenn eine lange Transaktion das DDL blockiert.
Verwenden Sie für sensible Systeme ein Migrationstool wie pt-online-schema-change oder gh-ost oder planen Sie das DDL während eines verkehrsarmen Zeitfensters.
Eine praktische Routine zur Indexüberprüfung
Wenn ich eine langsame MySQL-Abfrage überprüfe, verwende ich diese Reihenfolge:
- Erfassen Sie das genaue SQL mit echten Bindungswerten.
- Führen Sie
EXPLAINund, wo sicher,EXPLAIN ANALYZEaus. - Überprüfen Sie, ob die vorhandenen Indizes zum
WHERE-,JOIN- undORDER BY-Muster passen. - Fügen Sie den kleinsten nützlichen zusammengesetzten Index in der Staging-Umgebung hinzu.
- Vergleichen Sie untersuchte Zeilen, Abfragezeit und Schreibauswirkung.
- Rollen Sie vorsichtig aus und beobachten Sie die Slow-Query-Logs und die Replikationsverzögerung.
Diese Routine hält die Indizierung ehrlich. Sie versuchen nicht, Indizes zu sammeln. Sie versuchen, die Arbeit zu reduzieren, die MySQL für die Abfragen leisten muss, die Ihre Anwendung tatsächlich ausführt.