Ein Datenbankindex ist eine sortierte Nachschlagestruktur, mit der die Engine passende Zeilen direkt findet, statt die ganze Tabelle zu durchsuchen. Der Vergleich mit dem Buch trifft genau: Niemand findet “Idempotenz” in einem 900-seitigen Buch, indem er es von vorn liest – man schlägt im Register nach und springt zur Seite. Datenbanken treffen bei jeder Abfrage dieselbe Wahl, und ob sie springen können, ist der häufigste Unterschied zwischen einer Abfrage mit 5 Millisekunden und einer mit 5 Sekunden.
Das Wichtigste in Kürze
| Frage | Antwort |
|---|---|
| Was es ist | Eine sortierte Nebenstruktur: Werte + Verweise, wie das Register eines Buches |
| Der Gewinn | Logarithmische Suche statt linearem Scan – entscheidend bei großen Datenmengen |
| Der Preis | Jeder Schreibvorgang aktualisiert jeden Index; der Speicherbedarf wächst |
| Was indiziert wird | Filter-, Join-/Pointer- und Sortierspalten – sofern selektiv |
| Die Diagnose | EXPLAIN: Ein vollständiger Scan auf einer großen Tabelle ist das Warnsignal |
Der Index, angelegt und spürbar
-- Das Arbeitspferd: ein zusammengesetzter Index nach dem Muster der heißen Abfrage
CREATE INDEX orders_pending ON orders (status, created_at);
-- Spezialisierungen derselben Idee:
CREATE UNIQUE INDEX users_email ON users (email); -- Constraint + Tempo
CREATE INDEX unshipped ON orders (created_at)
WHERE shipped = false; -- partiell: nur die heiße Teilmenge
-- Vorher/nachher, laut Ausführungsplan:
EXPLAIN SELECT * FROM orders WHERE status = 'pending' AND created_at > now() - interval '1 day';
-- vorher: Seq Scan on orders (untersuchte Zeilen: 4.812.309)
-- nachher: Index Scan using orders_pending (untersuchte Zeilen: 1.214)
Anwendungscode unterliegt derselben Physik über die Form der Abfrage – diese Abfrage ist die Spezifikation des Index (status, createdAt), egal auf welcher Plattform sie geschrieben wird:
// JavaScript / Node.js — Back4app JS SDK
// The query shape tells you the index: (status, createdAt)
const query = new Parse.Query('Order');
query.equalTo('status', 'pending'); // equality first…
query.greaterThan('createdAt', since); // …then the range
query.descending('createdAt'); // …sorted by the same column
const backlog = await query.find();
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // Flutter / Dart — Back4app Flutter SDK
// The query shape tells you the index: (status, createdAt)
final query = QueryBuilder<ParseObject>(ParseObject('Order'))
..whereEqualTo('status', 'pending') // equality first…
..whereGreaterThan('createdAt', since) // …then the range
..orderByDescending('createdAt'); // …sorted by the same column
final response = await query.query();
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // iOS / Swift — Back4app Swift SDK
// The query shape tells you the index: (status, createdAt)
let query = Order.query("status" == "pending", "createdAt" > since)
.order([.descending("createdAt")])
query.find { result in
if case .success(let backlog) = result { render(backlog) }
}
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // Android / Kotlin — Back4app Android SDK
// The query shape tells you the index: (status, createdAt)
val query = ParseQuery.getQuery<ParseObject>("Order")
query.whereEqualTo("status", "pending") // equality first…
query.whereGreaterThan("createdAt", since) // …then the range
query.orderByDescending("createdAt") // …sorted by the same column
query.findInBackground { backlog, e -> if (e == null) render(backlog) }
// Indexed: milliseconds at any size. Unindexed: a full collection scan. Wie der Sprung funktioniert
Die Standardstruktur ist überall der B-Baum (B-Tree): flach, sortiert, selbstbalancierend – drei oder vier Ebenen decken Hunderte Millionen Einträge ab, und seine Blätter sind verkettet. Deshalb bedient eine einzige Struktur Gleichheit, Bereiche und ORDER BY gleichermaßen. Diese Vielseitigkeit ist der Grund für den bewährten Rat “im Zweifel B-Baum” – die Alternativen sind Spezialisten.
B-Baum vs. Hash vs. die Spezialisten
Welchen Indextyp sollten Sie verwenden?
| Typ | Unterstützt | Die richtige Wahl, wenn |
|---|---|---|
| B-Baum (Standard) | =, <, >, Bereiche, Sortierungen, Präfixe | Fast immer – der Generalist |
| Hash | Nur Gleichheit, O(1) | Exakte Treffer, sonst nichts |
| Zusammengesetzt | Mehrere Spalten, Leftmost-Prefix | Die heiße Abfrage filtert auf mehreren Spalten |
| Eindeutig (Unique) | B-Baum + keine Duplikate | Constraint und Index in einem |
| Partiell | Ein gefilterter Ausschnitt der Zeilen | Heiße Teilmengen: nicht versandt, ungelesen, aktiv |
| Abdeckend (Covering) | Abfrage wird allein aus dem Index beantwortet | Leseintensive Abfragen mit stabiler Spaltenliste |
| Volltext (invertiert) | Wörter → Dokumente | Suchfelder |
| Geodaten | Punkte, Regionen, Entfernung | ”In meiner Nähe”-Abfragen |
Zwei Regeln tragen die Zeile für zusammengesetzte Indizes: die Leftmost-Prefix-Regel – ein Index auf (a, b, c) bedient a, (a, b) und (a, b, c), aber nie b allein – und Gleichheit zuerst, Bereich/Sortierung zuletzt in der Spaltenreihenfolge (die Eselsbrücke ESR bei der Indizierung in Dokumentdatenbanken, wo dieselben B-Bäume dieselbe Arbeit leisten).
Wann indizieren – und wann nicht
Indizieren Sie: Spalten in häufigen WHERE-Filtern; Join-Schlüssel und Pointer-Felder (manche Engines indizieren Fremdschlüssel nie automatisch – ein klassischer stiller Scan); ORDER BY-Spalten auf heißen Pfaden; und immer mit Blick auf die Selektivität – eine E-Mail-Spalte (Millionen unterschiedlicher Werte) ist ihren Index wert, ein Status-Flag (drei Werte) allein meist nicht, obwohl es an erster Stelle eines zusammengesetzten Index mit einem Bereich dahinter glänzt. Indizieren Sie nicht: kleine Tabellen, die die Engine schneller scannt, als sie sucht; schreiblastige Tabellen über die wenigen unverzichtbaren Indizes hinaus; Spalten, die bereits das linke Präfix eines bestehenden Index abdeckt; und alles, wofür Sie keine Abfrage benennen können – ein Index ohne Abfrage ist reiner Aufschlag auf jeden Schreibvorgang. Der Prüfzyklus: Langsame Abfragen mit EXPLAIN auf Scans untersuchen, Nutzungsstatistiken auf tote Indizes prüfen, entsprechend anlegen und löschen.
Typische Anwendungsfälle
- Die Abfrage für heiße Listen. Status- und Datumsfilter hinter jedem Dashboard und jedem Feed – das Heimspiel des zusammengesetzten Index.
- Anmelde- und Suchfelder. E-Mail, Benutzername, externe IDs – eindeutige Indizes, die Constraint und Tempo zugleich liefern.
- Join- und Pointer-Pfade. Jeder Fremdschlüssel und jeder Pointer, den Ihre Abfragen durchlaufen; der stille Komplize des N+1-Problems ist ein nicht indizierter.
- Multi-Tenant-Filter. Zusammengesetzte Indizes mit dem Tenant an erster Stelle – die Regel der Multi-Tenant-Architektur, nach der jeder Index mit
tenant_idbeginnt. - Suche und Geodaten. Invertierte und räumliche Indizes für die Abfragen, die B-Bäume nicht können.
Sollten Sie diesen Index anlegen? Eine Entscheidungsmatrix
| Anlegen, wenn … | Weglassen, wenn … |
|---|---|
| Eine häufige Abfrage auf der Spalte filtert oder sortiert | Keine Abfrage in Produktion sie verwendet |
| EXPLAIN Scans auf einer wachsenden Tabelle zeigt | Die Tabelle klein ist und klein bleibt |
| Die Spalte selektiv ist (viele unterschiedliche Werte) | Das Präfix eines bestehenden Index sie bereits abdeckt |
| Sie ein Join-Schlüssel oder Pointer-Feld ist | Die Tabelle schreiblastig und der Lesezugriff selten ist |
| Ohnehin eine Eindeutigkeitsregel durchgesetzt werden muss | Sie raten – messen Sie zuerst |
Die Disziplin in einem Satz: Indizes werden als Antwort auf Abfragen angelegt, anhand ihrer Nutzung überprüft und ohne Sentimentalität gelöscht.
Grenzen und Trade-offs
- Schreibvorgänge bezahlen die Lesevorgänge. Jeder Index ist eine weitere Struktur, die jeder Schreibvorgang pflegen muss – Massenimporte laufen bekanntlich schneller, wenn man die Indizes vorher löscht und danach neu aufbaut.
- Speicher kostet. Indizes erreichen oft die Größe der Tabelle selbst; gerade abdeckende Indizes tauschen Plattenplatz gegen Tempo.
- Der Optimizer entscheidet, nicht Sie. Ein Index mit geringer Selektivität wird womöglich zu Recht ignoriert; veraltete Statistiken können einen guten zu Unrecht ignorieren – der Plan ist die Wahrheit.
- Fehler in der Reihenfolge machen zusammengesetzte Indizes wirkungslos.
(created_at, status)und(status, created_at)sind verschiedene Werkzeuge; die Leftmost-Prefix-Regel verzeiht nichts. - Indizes reparieren keine Abfrage. Führende Wildcards, Funktionen über Spalten und N+1-Schleifen hebeln die Indizierung von oben aus – die Form der Abfrage kommt zuerst.
Datenbankindizes mit Back4app
Back4app ist eine Open-Source-Plattform für Backend as a Service (BaaS), die eine verwaltete Datenbank, automatisch generierte REST- und GraphQL-APIs, Authentifizierung, Dateispeicher und Serverless-Funktionen mit Cloud Code kombiniert. Die Indexverwaltung liegt beim Schema im Dashboard: Legen Sie Indizes pro Klasse an – auf einem Feld oder zusammengesetzt –, prüfen und löschen Sie sie, und zwar auf dem MongoDB-basierten Speicher, den Ihre SDK-Abfragen tatsächlich treffen, nach derselben B-Baum- und ESR-Logik wie oben. Die Abfrage in den Code-Tabs und der Index, der sie bedient, entstehen am selben Ort – genau dort, wo die Disziplin “Indizieren Sie, was Sie abfragen” hingehört.
Häufige Fragen
Was ist ein Datenbankindex, einfach erklärt?
Eine separate, sortierte Struktur – meist ein B-Baum –, die Spaltenwerte samt Verweisen auf ihre Zeilen enthält, genau wie das Register eines Buches Begriffe samt Seitenzahlen enthält. Die Engine sucht den Wert in der kleinen sortierten Struktur und springt direkt zu den passenden Zeilen, statt die ganze Tabelle in der Hoffnung zu lesen, sie irgendwo zu finden.
Wie viel schneller ist eine indizierte Abfrage?
Es ist der Unterschied zwischen logarithmischem und linearem Aufwand: Eine Suche im B-Baum berührt eine Handvoll Seiten, ob die Tabelle zehntausend oder hundert Millionen Zeilen enthält, während ein vollständiger Scan alles liest. Bei kleinen Datenmengen fühlt sich beides sofort an – deshalb verstecken sich fehlende Indizes in der Entwicklung und explodieren in Produktion, wo dieselbe Abfrage plötzlich Millionen Zeilen untersucht.
Verlangsamen Indizes Schreibvorgänge?
Ja – das ist der Preis. Jedes Insert, Update oder Delete auf einer indizierten Spalte muss auch jeden Index aktualisieren, der sie enthält: Sechs Indizes auf einer Tabelle bedeuten bis zu sechs zusätzliche Strukturänderungen pro Schreibvorgang, dazu den Speicherplatz, den sie belegen. Indizes kaufen Lesegeschwindigkeit, bezahlt mit Schreibgeschwindigkeit und Plattenplatz – bewusst angelegte verdienen das, vergessene verursachen nur Kosten.
Welche Spalten sollte man indizieren?
Die Spalten, die Ihre Abfragen tatsächlich verwenden: Filter (WHERE), Join-Schlüssel – einschließlich Fremdschlüsseln und Pointer-Feldern, die manche Engines nicht automatisch indizieren – und Sortierspalten (ORDER BY). Prüfen Sie zusätzlich die Selektivität: Eine E-Mail-Spalte, die Millionen Zeilen unterscheidet, rechtfertigt ihren Index; ein boolesches Flag, das die Tabelle in zwei Hälften teilt, meist nicht.
In welcher Reihenfolge gehören die Spalten eines zusammengesetzten Index?
Gleichheitsbedingungen zuerst, Bereiche und Sortierung danach – und denken Sie an die Leftmost-Prefix-Regel: Ein Index auf (a, b, c) bedient Abfragen, die auf a, auf a und b oder auf alle drei filtern, aber nicht auf b allein. In Dokumentdatenbanken heißt dieselbe Regel ESR: Equality, Sort, Range. Die Spaltenreihenfolge entscheidet, ob ein zusammengesetzter Index wirkt oder bloß existiert.
Wie finde ich fehlende oder ungenutzte Indizes?
Für fehlende: Lassen Sie sich den Ausführungsplan zeigen – EXPLAIN – und suchen Sie nach vollständigen Scans auf großen Tabellen; die Filterspalte einer langsamen, häufigen Abfrage ist der Kandidat. Für ungenutzte: Jede Engine führt Nutzungsstatistiken für Indizes, und ein Index, den seit Monaten keine Abfrage berührt hat, ist reiner Aufschlag auf jeden Schreibvorgang – löschen Sie ihn. Plan und Statistik zusammen sind die ganze Methodik.
Was sind eindeutige, partielle und abdeckende Indizes?
Spezialisierungen derselben Struktur: Ein eindeutiger Index (Unique Index) erzwingt als Constraint, dass es keine Duplikate gibt, und beschleunigt zugleich die Suche; ein partieller Index erfasst nur Zeilen, die eine Bedingung erfüllen – klein und schnell für heiße Teilmengen wie nicht versandte Bestellungen; ein abdeckender Index (Covering Index) enthält alle Spalten, die eine Abfrage braucht, sodass die Engine allein aus dem Index antworten kann, ohne die Tabelle zu lesen.
Funktioniert Indizierung in Dokumentdatenbanken genauso?
Konzeptionell identisch – B-Bäume über Feldwerte – mit denselben Trade-offs und derselben Leftmost-Prefix-Logik unter der Faustregel ESR. Dokumentdatenbanken ergänzen Multikey-Indizes über Array-Felder und Geodaten-Varianten, und die Betriebsregel gilt unverändert: Indizieren Sie, was Sie abfragen, besonders die Pointer-Felder, über die Joins und Includes laufen.