SQL Indizes Performance
SQL Indizes: Datenbankabfragen beschleunigen
Ohne Indizes durchsucht die Datenbank jede Zeile (Full Table Scan). Mit dem richtigen Index werden Abfragen um Größenordnungen schneller.
Was ist ein Index?
Ein Index ist wie ein Buchindex: Statt das ganze Buch zu durchsuchen, schlagen Sie im Index nach und springen direkt zur Seite.
Ohne Index: SELECT * FROM users WHERE email = 'max@example.com'; → Durchsucht alle 1.000.000 Zeilen (Full Table Scan) → Zeit: 2-3 Sekunden Mit Index auf email: → Index-Lookup: wenige Schritte → Zeit: <1 Millisekunde
Index erstellen
-- Einfacher Index
CREATE INDEX idx_users_email ON users(email);
-- Unique Index (keine Duplikate)
CREATE UNIQUE INDEX idx_users_email ON users(email);
-- Composite Index (mehrere Spalten)
CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
-- Index löschen
DROP INDEX idx_users_email ON users;
-- Index in Tabellendefinition
CREATE TABLE users (
id INT PRIMARY KEY, -- automatisch indexiert
email VARCHAR(255) UNIQUE, -- automatisch indexiert
name VARCHAR(100),
created_at TIMESTAMP,
INDEX idx_created (created_at)
);
B-Tree Index (Standard)
Der häufigste Index-Typ. Gut für:
- Gleichheit:
WHERE email = 'x' - Bereich:
WHERE age BETWEEN 20 AND 30 - Sortierung:
ORDER BY created_at - LIKE mit Präfix:
WHERE name LIKE 'Max%'
[M]
/ \
[D] [S]
/ \ / \
[A] [G] [O] [W]
↓ ↓ ↓ ↓
Anna ... ... ...
Composite Index (mehrspaltig)
Die Spaltenreihenfolge ist entscheidend!
CREATE INDEX idx_orders ON orders(customer_id, order_date, status); -- ✅ Index wird genutzt (von links nach rechts) SELECT * FROM orders WHERE customer_id = 123; SELECT * FROM orders WHERE customer_id = 123 AND order_date = '2024-01-01'; SELECT * FROM orders WHERE customer_id = 123 AND order_date > '2024-01-01'; -- ❌ Index wird NICHT oder nur teilweise genutzt SELECT * FROM orders WHERE order_date = '2024-01-01'; -- Erste Spalte fehlt! SELECT * FROM orders WHERE status = 'pending'; -- Erste Spalten fehlen!
Faustregel für Composite Index
- Spalten mit Gleichheit zuerst
- Dann Bereichsabfragen
- Dann Sortierung
-- Query: SELECT * FROM orders WHERE customer_id = 123 AND order_date > '2024-01-01' ORDER BY status; -- Optimaler Index: CREATE INDEX idx ON orders(customer_id, order_date, status);
Wann Indizes verwenden?
- WHERE-Klauseln
- JOIN-Spalten (Foreign Keys)
- ORDER BY-Spalten
- GROUP BY-Spalten
- Spalten mit hoher Selektivität
- Kleine Tabellen (<1000 Zeilen)
- Spalten die selten abgefragt werden
- Spalten mit wenigen unterschiedlichen Werten (z.B. Boolean)
- Tabellen mit vielen INSERT/UPDATE-Operationen
EXPLAIN analysieren
EXPLAIN SELECT * FROM users WHERE email = 'max@example.com'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | users | ref | idx_email |idx_email| 767 | const| 1 | Using index | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
Wichtige type-Werte
| type | Bedeutung | Performance |
|---|---|---|
const/eq_ref |
Primärschlüssel/Unique Lookup | ⭐⭐⭐⭐⭐ |
ref |
Index-Lookup | ⭐⭐⭐⭐ |
range |
Index-Bereichsscan | ⭐⭐⭐ |
index |
Full Index Scan | ⭐⭐ |
ALL |
Full Table Scan | ⭐ (schlecht) |
Index-Killer vermeiden
-- ❌ Funktion auf Spalte SELECT * FROM users WHERE YEAR(created_at) = 2024; -- ✅ Besser: SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'; -- ❌ LIKE mit führender Wildcard SELECT * FROM users WHERE email LIKE '%@gmail.com'; -- ✅ Funktioniert: SELECT * FROM users WHERE email LIKE 'max%'; -- ❌ Implizite Typkonvertierung SELECT * FROM users WHERE phone = 123456; -- phone ist VARCHAR! -- ✅ Besser: SELECT * FROM users WHERE phone = '123456'; -- ❌ OR kann Index verhindern SELECT * FROM users WHERE email = 'a' OR name = 'b'; -- ✅ Besser (zwei Indizes nutzen): SELECT * FROM users WHERE email = 'a' UNION SELECT * FROM users WHERE name = 'b';
Covering Index
Wenn alle benötigten Spalten im Index sind, muss die Tabelle nicht gelesen werden:
CREATE INDEX idx_covering ON users(email, name, created_at); -- Query liest NUR aus Index (Extra: "Using index") SELECT email, name FROM users WHERE email = 'max@example.com';
Index-Statistiken
-- MySQL: Indizes einer Tabelle SHOW INDEX FROM users; -- PostgreSQL SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users'; -- Statistiken aktualisieren ANALYZE TABLE users; -- MySQL ANALYZE users; -- PostgreSQL