Was ist MySQL?
MySQL ist eine Open-Source-relationale Datenbank, die zur Standard-Storage-Layer des frühen Webs wurde. Sie wurde 1995 mit einem Fokus auf Geschwindigkeit und Einfachheit für leseintensive Seiten veröffentlicht und wuchs zusammen mit dem LAMP-Stack — Linux, Apache, MySQL und PHP — zu einer der am weitesten verbreiteten Datenbanken überhaupt heran.
Oracle entwickelt sie mittlerweile weiter, aber eine große Community und eine Vielzahl an Managed Services sorgen dafür, dass sie omnipräsent bleibt. WordPress, Magento, E-Commerce-Plattformen im Stil von Shopify und zahllose maßgeschneiderte Anwendungen laufen auf MySQL. Wenn Sie eine Website mit einem Login-Formular genutzt haben, ist die Chance groß, dass eine MySQL-Tabelle involviert war.
Das moderne MySQL ist nicht mehr die einfache Engine der 1990er Jahre. Seit Version 8 verfügt es über ein transaktionales Data Dictionary, Common Table Expressions, Window Functions und einen leistungsfähigen JSON-Support. MySQL heute zu lernen bedeutet, eine ernstzunehmende relationale Datenbank zu erlernen, die zudem außerordentlich gut unterstützt wird.
Wo MySQL läuft
Der größte praktische Vorteil von MySQL ist seine Allgegenwärtigkeit. Fast jeder Shared-Hoster, Cloud-Provider und Platform-as-a-Service bietet es an. Frameworks liefern passende Treiber mit, ORMs unterstützen es nativ und DBAs verfügen über jahrzehntelange Erfahrung damit. Das reduziert die Kosten für alles rund um die Datenbank: Recruiting, Tooling, Monitoring und Migration.
Es ist die Standardwahl für Content-Management-Systeme, E-Commerce, SaaS-Backends und jede Anwendung, bei der das Ökosystem genauso wichtig ist wie die Engine selbst. Es skaliert von einer einzelnen kleinen Instanz bis hin zu sharded clusters, und der Weg von der einen zur anderen Variante ist bestens dokumentiert.
InnoDB ist die entscheidende Engine
MySQL verfügt über eine plugbare Storage-Engine-Architektur, aber in der Praxis werden Sie InnoDB verwenden. Diese bietet:
- ACID-Transaktionen mit
COMMITundROLLBACK. - Row-level locking, sodass Schreibvorgänge keine Lesevorgänge blockieren.
- Foreign keys und die Durchsetzung von Constraints.
- Crash recovery über ein Redo-Log.
- MVCC für konsistente Reads.
Der ältere MyISAM-Engine fehlten Transaktionen und Row-level locking. In einem neuen Schema hat sie nichts zu suchen. Deklarieren Sie ENGINE=InnoDB immer explizit, damit die Wahl sichtbar ist und nicht von den Server-Defaults abhängt.
CREATE TABLE products (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
sku VARCHAR(64) NOT NULL,
name VARCHAR(200) NOT NULL,
price_cents INT UNSIGNED NOT NULL,
stock INT NOT NULL DEFAULT 0,
attributes JSON NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uniq_products_sku (sku)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Datentypen und AUTO_INCREMENT
Das Typsystem von MySQL ist pragmatisch. Die gängigsten Optionen:
INTundBIGINT— ganze Zahlen; verwenden SieUNSIGNEDfür IDs und Zähler, die nicht negativ sein dürfen.VARCHAR(n)— Text mit variabler Länge und einem Maximum. Im Gegensatz zu Postgres profitiert MySQL tatsächlich von einer sinnvollen Längenangabe.TEXT— große Texte, die bei Bedarf außerhalb der Zeile gespeichert werden.DECIMAL(p, s)— exakte Dezimalzahlen; der richtige Typ für Geldbeträge, sofern Sie keine Integer-Minor-Units verwenden.TIMESTAMPundDATETIME— Zeitstempel;TIMESTAMPkonvertiert in UTC und hat eine Bereichsbeschränkung, währendDATETIMEdie Werte genau so speichert, wie sie übergeben werden.JSON— ein validiertes JSON-Dokument, das effizient gespeichert wird.ENUM— eine feste Menge an Strings; praktisch, aber eine Änderung der Liste erfordert eine Schema-Änderung.
AUTO_INCREMENT generiert die nächste Ganzzahl für eine Spalte, fast immer den Primary Key. Es ist schnell und toleriert Lücken: Rollbacks bei Inserts verbrauchen einen Wert, und gleichzeitige Inserts führen unter Umständen nicht zu fortlaufenden Nummern. Verlassen Sie sich niemals darauf, dass die ID lückenlos ist oder dass ihre Reihenfolge eine bestimmte Bedeutung hat.
INSERT INTO products (sku, name, price_cents, stock, attributes)
VALUES ('SKU-1', 'Widget', 999, 10, JSON_OBJECT('colour', 'blue'));
SELECT LAST_INSERT_ID();
CRUD ohne Überraschungen
Die vier Grundoperationen lassen sich auf vier Statements übertragen. Betrachten Sie diese als Set, da sich das Muster wiederholt.
INSERT INTO products (sku, name, price_cents, stock, attributes)
VALUES ('SKU-2', 'Gadget', 1499, 5, JSON_OBJECT('colour', 'red'));
SELECT id, sku, name, price_cents
FROM products
WHERE stock > 0
ORDER BY price_cents ASC
LIMIT 20 OFFSET 40;
UPDATE products
SET price_cents = 1299, stock = stock - 1
WHERE id = 42;
DELETE FROM products
WHERE stock = 0 AND created_at < NOW() - INTERVAL 90 DAY;
Zwei Gewohnheiten verhindern die klassischen Fehler. Erstens: Fügen Sie bei UPDATE und DELETE immer eine WHERE-Klausel hinzu; ohne diese wird jede einzelne Zeile geändert. Zweitens: Führen Sie zuerst das entsprechende SELECT aus, um zu bestätigen, welche Zeilen Sie gerade beeinflussen werden. MySQL verfügt über einen sql_safe_updates-Modus, der Statements ohne Key in der WHERE ablehnt – dies in der Entwicklung zu aktivieren, ist eine einfache und effektive Sicherheitsmaßnahme.
LIMIT mit OFFSET ermöglicht die Paginierung, allerdings scannen große Offsets Zeilen, nur um sie dann wieder zu verwerfen. Für eine tiefe Paginierung ist Keyset-Pagination vorzuziehen: WHERE id > :last_id ORDER BY id LIMIT 20.
Joins und Aggregation
Joins kombinieren Tabellen basierend auf einer Übereinstimmungsbedingung, genau wie in Standard-SQL.
SELECT c.name AS category,
COUNT(*) AS product_count,
SUM(p.price_cents) AS inventory_value
FROM products p
JOIN categories c ON c.id = p.category_id
WHERE p.stock > 0
GROUP BY c.id, c.name
ORDER BY inventory_value DESC
LIMIT 20;
JOIN behält passende Paare bei, LEFT JOIN behält alle Zeilen der linken Tabelle und füllt fehlende Spalten der rechten Tabelle mit NULL auf. Aggregate wie COUNT, SUM, AVG, MIN und MAX fassen Gruppen zusammen; jede ausgewählte Spalte, die nicht aggregiert wird, muss gruppiert werden.
MySQL erlaubte es historisch, nicht gruppierte Spalten auszuwählen, und gab einen beliebigen Wert zurück, was Bugs verschleierte. Wenn ONLY_FULL_GROUP_BY aktiviert ist – der Standard in MySQL 8 –, lehnt der Server mehrdeutige Abfragen ab, was genau das gewünschte Verhalten ist. Deaktivieren Sie dies nicht, nur um eine alte Abfrage zum Laufen zu bringen; korrigieren Sie stattdessen die Abfrage.
WHERE filtert Zeilen vor der Gruppierung und HAVING filtert danach, daher gehören Bedingungen für Aggregate in HAVING.
Indexe und EXPLAIN
Ein Index ist eine sortierte Struktur, die einen Full Table Scan vermeidet. MySQL erstellt automatisch einen Index für den Primary Key und für jeden UNIQUE-Constraint. Fügen Sie weitere Indexe für die Spalten hinzu, nach denen Sie filtern, joinen oder sortieren.
CREATE INDEX idx_products_price ON products (price_cents);
Ein Composite Index deckt mehrere Spalten ab und folgt der Leftmost-Prefix-Regel: Ein Index auf (category_id, price_cents) hilft bei Abfragen, die nach category_id filtern, oder nach category_id und price_cents, aber nicht nach price_cents allein. Ordnen Sie die Spalten so an, dass die selektivsten und am häufigsten gefilterten Spalten außen stehen.
Fragen Sie den Optimizer, was er tun wird:
EXPLAIN
SELECT id, name, price_cents
FROM products
WHERE price_cents < 2500
ORDER BY price_cents
LIMIT 25;
Lesen Sie zuerst die Spalte type: const, eq_ref und ref sind gut; range ist akzeptabel; index und ALL bedeuten einen Scan. Die Spalte key zeigt an, welcher Index gewählt wurde, und rows schätzt, wie viele Datensätze geprüft werden. Ein großer Wert in rows bei einem kleinen Ergebnis deutet meist auf einen fehlenden oder unbrauchbaren Index hin. Verwenden Sie in MySQL 8 EXPLAIN ANALYZE, um die Abfrage auszuführen und die tatsächlichen Timings zu sehen.
Covering Indexes verdienen eine Erwähnung. Wenn ein Index jede Spalte enthält, die eine Abfrage benötigt, kann MySQL die Antwort allein aus dem Index liefern und muss die eigentliche Zeile nie anfassen. Eine Spalte nur deshalb zu einem Index hinzuzufügen, um ihn zu einem Covering Index zu machen, bringt oft einen erheblichen Performance-Gewinn.
utf8mb4 und die Charset-Falle
Zeichensätze sind ein Bereich, in dem MySQL oft für Überraschungen sorgt. Über einen Großteil seiner Geschichte war der Standard-utf8 auf maximal drei Bytes pro Zeichen beschränkt. Das deckt zwar die meisten Texte ab, aber keine vier-Byte-Codepoints wie Emojis oder viele seltene Schriften. Der Versuch, ein Emoji in einer utf8-Spalte zu speichern, führt je nach Server-Modus entweder zu einem Fehler oder dazu, dass der Wert abgeschnitten wird.
Die Lösung ist utf8mb4, welches echtes UTF-8 ist und alles speichern kann. Setzen Sie dies auf jeder Ebene – Server, Datenbank, Tabelle und Verbindung:
CREATE DATABASE shop
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
Die Collation (Kollation) bestimmt, wie Strings verglichen und sortiert werden. utf8mb4_0900_ai_ci ist akzent- und case-insensitive, was in der Regel den Erwartungen der Nutzer bei einer Suche entspricht. Collations beeinflussen zudem das Verhalten von Indizes. Halten Sie diese daher bei verknüpften Spalten konsistent; eine Diskrepanz erzwingt Konvertierungen, die einen Index unbrauchbar machen können.
Transaktionen und Isolationsstufen
InnoDB bietet Transaktionen an. Gruppieren Sie zusammengehörige Schreibvorgänge, sodass diese gemeinsam erfolgreich abgeschlossen werden oder gemeinsam fehlschlagen.
START TRANSACTION;
UPDATE inventory SET quantity = quantity - 1
WHERE product_id = 42 AND quantity >= 1;
INSERT INTO orders (product_id, quantity)
VALUES (42, 1);
COMMIT;
Prüfen Sie die betroffenen Zeilen und ROLLBACK, falls das geschützte Update nichts geändert hat. Die Standard-Isolationsstufe ist Repeatable Read, welche einen konsistenten Snapshot für die Transaktion bereitstellt und in InnoDB Gap Locks setzt, um Phantom-Zeilen zu verhindern. Die anderen Stufen sind Read Uncommitted, Read Committed und Serializable.
Ein wichtiger Unterschied zu Postgres: Aufgrund des Gap Lockings unter Repeatable Read können gleichzeitige Inserts in einen Bereich leichter zu Blockaden oder Deadlocks führen. Halten Sie Transaktionen kurz, aktualisieren Sie Zeilen in einer konsistenten Reihenfolge und stellen Sie sich darauf ein, einen Deadlock zu wiederholen – InnoDB meldet diesen als Fehler, anstatt den Zustand zu korrumpieren.
Replikation und Read-Scaling
Die meisten MySQL-Deployments skalieren Lesezugriffe (Reads) vor Schreibzugriffen (Writes). Die Primary-Instanz zeichnet jede Änderung in ihrem Binary Log auf, und eine oder mehrere Replicas verbinden sich damit, um dieses Log zu reproduzieren. Standardmäßig ist die Replikation asynchron, sodass eine Replica der Primary um Millisekunden oder mehr hinterherhinken kann.
Das gängige Muster besteht darin, Schreibvorgänge an die Primary zu senden und Lesezugriffe auf die Replicas zu verteilen, wobei akzeptiert wird, dass ein Read kurzzeitig leicht veraltete Daten zurückgeben kann. Für eine Read-after-Write-Konsistenz sollten die Lesezugriffe eines Nutzers für ein kurzes Zeitfenster nach seinem Schreibvorgang an die Primary geroutet werden, oder die Replica nur für Daten genutzt werden, bei denen ein Lag tolerierbar ist.
Replikation bietet zudem Hochverfügbarkeit. Wenn die Primary ausfällt, kann eine Replica befördert werden. Tools wie Orchestratoren und Managed Services automatisieren diesen Failover, aber man muss den Trade-off zwischen synchronen und asynchronen Modi verstehen: Der synchrone Modus wartet auf die Replicas und gefährdet die Verfügbarkeit, während der asynchrone Modus das Risiko birgt, bei einem Failover die letzten paar Transaktionen zu verlieren.
Upserts mit ON DUPLICATE KEY UPDATE
Das idiomatische Upsert in MySQL ist INSERT ... ON DUPLICATE KEY UPDATE. Wenn das Insert einen Primary Key oder Unique Key verletzen würde, wird stattdessen die Update-Klausel ausgeführt.
INSERT INTO products (sku, name, price_cents, stock, attributes)
VALUES ('SKU-1', 'Widget', 1099, 5, JSON_OBJECT('colour', 'blue'))
ON DUPLICATE KEY UPDATE
price_cents = VALUES(price_cents),
stock = stock + VALUES(stock);
Dies ist atomar, was besonders bei Countern und Lagerbeständen wichtig ist. Die Alternative – zuerst ein Select auszuführen und dann zu entscheiden, ob ein Insert oder Update nötig ist – birgt ein Race-Condition-Fenster, in dem zwei Sessions gleichzeitig feststellen, dass keine Zeile existiert, und beide ein Insert ausführen. Nutzen Sie das Upsert immer dann, wenn die Operation tatsächlich ein „Insert-oder-Update“ ist.
JSON-Spalten
Der Typ JSON in MySQL speichert ein validiertes Dokument und unterstützt Funktionen, um Teile davon zu lesen und zu schreiben.
SELECT id, name,
attributes->>'$.colour' AS colour
FROM products
WHERE attributes->>'$.colour' = 'blue';
Sie können JSON mithilfe von generierten Spalten indexieren. Da MySQL einen JSON-Ausdruck nicht direkt indexieren kann, extrahieren Sie diesen in eine gespeicherte generierte Spalte und indexieren Sie diese:
ALTER TABLE products
ADD COLUMN colour VARCHAR(32)
GENERATED ALWAYS AS (attributes->>'$.colour') STORED,
ADD INDEX idx_products_colour (colour);
Verwenden Sie JSON für variable Attribute und Payloads von Drittanbietern. Behalten Sie Felder, nach denen Sie ständig filtern, als echte Spalten bei; der Trick mit den generierten Spalten funktioniert zwar, aber eine native Spalte mit einem echten Typ ist einfacher und schneller.
Stored Procedures: sparsam einsetzen
MySQL unterstützt stored procedures, Funktionen, Trigger und geplante Events. Diese können die Anzahl der Round Trips reduzieren und Logik zentralisieren, verschieben die Business-Logik jedoch in eine Sprache, die schwerer zu testen, zu versionieren und zu debuggen ist als Ihr Anwendungscode.
Nutzen Sie diese für Aufgaben, die wirklich in die Datenbank gehören: Massenwartung, Datenmigrationen und Jobs, die nah an den Daten ausgeführt werden müssen. Vermeiden Sie es, Kernregeln Ihrer Domain in Procedures zu schreiben, die nur ein einziges Team versteht. Trigger werden besonders leicht vergessen; ein UPDATE, das stillschweigend drei Trigger auslöst, ist schwer nachvollziehbar, und versteckte Seiteneffekte überraschen früher oder später jeden.
Benutzer, Berechtigungen und Backups
Erstellen Sie einen dedizierten Anwendungsbenutzer, der nur die benötigten Privilegien besitzt, anstatt sich als root zu verbinden.
CREATE USER 'app'@'%' IDENTIFIED BY 'a-strong-password';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app'@'%';
FLUSH PRIVILEGES;
Halten Sie DDL und Migrationen in einem separaten, höher privilegierten Account. Schränken Sie nach Möglichkeit die erlaubten Hosts ein, fordern Sie TLS und rotieren Sie die Zugangsdaten.
Für Backups erstellt mysqldump einen logischen Dump, der einfach zu verschieben und wiederherzustellen ist:
mysqldump --single-transaction --routines --triggers shop > shop.sql
mysql shop_restore < shop.sql
Das Flag --single-transaction erstellt einen konsistenten Snapshot von InnoDB-Tabellen, ohne diese für den gesamten Dump zu sperren. Bei großen Datenbanken, bei denen die Dump-Zeit zu hoch ist, sollten Sie ein physisches Tool wie Percona XtraBackup verwenden. In jedem Fall sollten Sie die Position des Binary Logs aufzeichnen, um eine Point-in-Time-Recovery zu ermöglichen, und die Wiederherstellung regelmäßig testen.
MySQL vs MariaDB
MariaDB begann als Community-Fork von MySQL nach der Übernahme durch Oracle und hat sich seitdem weiterentwickelt. Es behält den Großteil der MySQL-Syntax bei und fügt eigene Storage Engines und Funktionen hinzu. MySQL hingegen hat sich seit Version 8 schnell vorwärtsbewegt, unter anderem mit einem neuen Data Dictionary, Window Functions und verbessertem JSON.
Für die meisten Anwendungen sind die Unterschiede gering. Entscheiden Sie sich für MySQL, wenn Ihr Managed Provider oder Ihr Support-Vertrag darauf aufbaut, und für MariaDB, wenn Sie ein Community-gesteuertes Projekt bevorzugen oder eine seiner speziellen Engines benötigen. Die relationalen Konzepte, SQL und die operationalen Muster sind zwischen beiden übertragbar, sodass die Entscheidung selten eine endgültige Sackgasse ist.
Volltextsuche in InnoDB
InnoDB wird mit einem Volltextindex ausgeliefert, sodass eine Suchfunktion keinen separaten Dienst benötigt. Erstellen Sie einen FULLTEXT-Index über die Textspalten und fragen Sie diesen mit MATCH ... AGAINST ab.
CREATE FULLTEXT INDEX ft_products_search
ON products (name, attributes);
SELECT id, name,
MATCH(name, attributes) AGAINST ('running shoe' IN NATURAL LANGUAGE MODE) AS score
FROM products
WHERE MATCH(name, attributes) AGAINST ('running shoe' IN NATURAL LANGUAGE MODE)
ORDER BY score DESC
LIMIT 20;
NATURAL LANGUAGE MODE sortiert nach Relevanz und ignoriert Wörter, die in den meisten Zeilen vorkommen. BOOLEAN MODE bietet Ihnen Operatoren wie +must, -exclude und "exact phrase" für eine präzisere Steuerung. Der Index hat eine minimale Token-Länge, die über innodb_ft_min_token_size gesteuert wird, sodass sehr kurze Wörter eventuell übersprungen werden.
Die Volltextsuche eignet sich gut für Produktkataloge und die Artikelsuche. Wechseln Sie zu einer dedizierten Engine, wenn Sie eine Toleranz gegenüber Tippfehlern, Faceting oder sprachübergreifende Analysen benötigen, die MySQL nicht bietet.
Views und generierte Spalten
Ein View ist eine benannte Abfrage, die sich wie eine Tabelle verhält. Dies ist nützlich, um eine gemeinsame Struktur zu kapseln und zu steuern, welche Spalten ein Reporting-User sehen kann.
CREATE VIEW in_stock AS
SELECT id, sku, name, price_cents, stock
FROM products
WHERE stock > 0;
SELECT * FROM in_stock WHERE price_cents < 2500;
MySQL 8 unterstützt Window-Funktionen, sodass Views auch analytische Ergebnisse vorstrukturieren können. Beachten Sie, dass ein View seine zugrunde liegende Abfrage jedes Mal neu ausführt, es sei denn, er wird manuell in eine Tabelle materialisiert; MySQL verfügt über keine nativen materialized views, weshalb Teams entweder eine Summary-Tabelle verwenden, die nach einem Zeitplan aktualisiert wird, oder ein Event nutzen.
Generierte Spalten, die im JSON-Abschnitt erläutert werden, sind die andere Seite dieses Konzepts: Eine stored generated column wird beim Schreiben berechnet und kann indexiert werden, während eine virtual column beim Lesen berechnet wird. Verwenden Sie eine stored column, wenn Sie den Ausdruck indexieren müssen, und eine virtual column, wenn Sie den Wert nur gelegentlich benötigen.
Schemas in einer Live-Datenbank ändern
ALTER TABLE in einer großen InnoDB-Tabelle kann dazu führen, dass die Tabelle neu aufgebaut wird und Sperren über einen langen Zeitraum gehalten werden. MySQL 8 unterstützt für viele Operationen Online DDL über ALGORITHM=INPLACE, wodurch die Tabelle in vielen Fällen neu aufgebaut wird, ohne gleichzeitige Lese- und Schreibzugriffe zu blockieren.
ALTER TABLE products
ADD COLUMN updated_at TIMESTAMP NULL,
ALGORITHM=INPLACE, LOCK=NONE;
ALTER TABLE products
ADD INDEX idx_products_updated (updated_at),
ALGORITHM=INPLACE, LOCK=NONE;
Nicht jede Änderung erfolgt online. Das Ändern eines Spaltentyps, das Hinzufügen eines FULLTEXT-Index oder das Neuaufbauen eines Primärschlüssels erfordert oft immer noch das Kopieren der Tabelle. Verwenden Sie in diesen Fällen Tools wie pt-online-schema-change oder gh-ost. Diese erstellen eine Schatten-Tabelle, kopieren die Zeilen in Batches und tauschen die Tabellen mit minimaler Blockierung aus. Unabhängig von der Methode sollten Schema-Änderungen über versionierte Migrationen durchgeführt und zuerst an einer Kopie der Produktionsdaten getestet werden.
Langsame Queries finden
MySQL zeichnet Queries, die long_query_time überschreiten, im slow query log auf, und EXPLAIN zeigt, wie eine spezifische Query ausgeführt wird. Zusammen sind sie der schnellste Weg von „die App ist langsam“ zu einem konkreten Fix.
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.2;
SHOW VARIABLES LIKE 'slow_query_log_file';
Das performance_schema und das sys Schema fassen dieselben Daten zusammen. Beginnen Sie mit den Statements, die die meiste Gesamtzeit beanspruchen – nicht mit der einzelnen langsamsten Query – und prüfen Sie EXPLAIN auf Full Scans bei großen Tabellen. SHOW PROFILE und EXPLAIN ANALYZE in MySQL 8 fügen Timings pro Stage hinzu, wenn Sie tiefer in die Analyse einsteigen müssen.
Daten schnell laden
Eine zeilenweise INSERT-Schleife ist der langsamste Weg, um Daten zu laden. Fassen Sie viele Zeilen in einem einzigen Statement zusammen; dies reduziert die Round-Trips und ermöglicht es InnoDB, Pages effizient zu schreiben.
INSERT INTO products (sku, name, price_cents, stock, attributes)
VALUES
('SKU-10', 'Widget', 999, 5, JSON_OBJECT('colour', 'blue')),
('SKU-11', 'Gadget', 1499, 3, JSON_OBJECT('colour', 'red')),
('SKU-12', 'Gizmo', 2499, 7, JSON_OBJECT('colour', 'green'));
Für Bulk-Imports streamt LOAD DATA INFILE eine Datei direkt in eine Tabelle und ist dramatisch schneller als jede INSERT-Form.
LOAD DATA LOCAL INFILE '/data/products.csv'
INTO TABLE products
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(sku, name, price_cents, stock, @attributes)
SET attributes = CAST(@attributes AS JSON);
Kapseln Sie große Ladevorgänge in eine Transaction, damit ein Fehler nicht zu einer halb importierten Tabelle führt. Erwägen Sie bei einem einmaligen Bulk-Load, sekundäre Indexes oder Unique-Checks zu deaktivieren und diese anschließend neu aufzubauen. Für alltägliche Schreibvorgänge in der Anwendung ist ein gebatchtes INSERT der richtige Standard.
Best Practices
- Verwende InnoDB für jede Tabelle und deklariere dies explizit.
- Erstelle Datenbanken, Tabellen und Verbindungen mit
utf8mb4und einer konsistenten Collation. - Speichere Geldbeträge als Integer-Kleinsteinheiten oder
DECIMAL, niemals alsFLOAToderDOUBLE. - Setze Indexe für die Abfragen, die du tatsächlich ausführst, und bestätige diese mit
EXPLAIN. - Behalte den Standard-
ONLY_FULL_GROUP_BY-Modus bei und schreibe korrekteGROUP BY-Queries. - Nutze
INSERT ... ON DUPLICATE KEY UPDATEfür atomare Upserts anstelle von Check-then-Act. - Halte Transaktionen kurz, aktualisiere Zeilen in einer stabilen Reihenfolge und führe bei einem Deadlock einen Retry aus.
- Weise der Anwendung einen User mit minimalen Berechtigungen (Least-Privilege) zu und führe DDL über einen Migrations-Account aus.
- Erstelle konsistente Backups mit
--single-transaction, protokolliere die Binlog-Position und teste die Wiederherstellung. - Bevorzuge Keyset-Pagination gegenüber großen
LIMIT ... OFFSET-Scans.
Häufige Fehler
- Tabellen auf MyISAM belassen und dadurch Transaktionen sowie Row-Level Locking verlieren.
- Das alte Drei-Byte-
utf8verwenden und sich anschließend wundern, warum Emojis nicht gespeichert werden. - Geldbeträge in
FLOATspeichern und Rundungsfehler ansammeln. UPDATEoderDELETEohneWHEREausführen und so die gesamte Tabelle neu schreiben.- Jede einzelne Spalte indexieren, anstatt nur die für die relevanten Queries, was die Schreibvorgänge verlangsamt.
- Sich darauf verlassen, dass
AUTO_INCREMENTlückenlos oder aussagekräftig ist. - Read-Modify-Write im Anwendungscode durchführen, anstatt einen atomaren Upsert zu nutzen.
- Unmittelbar nach einem Schreibvorgang von einer Replica lesen und veraltete Daten sehen.
ONLY_FULL_GROUP_BYdeaktivieren, um eine fehlerhafte Aggregation zu kaschieren.- Die Anwendung als
rootverbinden lassen.
Wie geht es weiter?
MySQL und seine Konzepte lassen sich hervorragend übertragen. Wenn Sie die andere große relationale Engine vergleichen möchten, lesen Sie den PostgreSQL-Guide, der dieselben Konzepte mit anderen Standardeinstellungen behandelt. Für die Sprache, die beiden zugrunde liegt, führt der SQL-Guide Joins, CTEs und Window-Functions von Grund auf ein. Und da die meisten MySQL-Deployments auf einen Cache setzen, sind Redis und das Dokumentenmodell in MongoDB natürliche Ergänzungen.