Document ID: /fineweb-2-swissfilter-quality_10-filterrobots/filtered/03159.jsonl.gz/2806

Was? Schon wieder überlastet? Jetzt haben wir doch gerade vier neue Server gekauft. Wollen wir nun weiter Hardware kaufen? Nein: Das erste, was wir in solchen Fällen machen, ist die Analyse des Slow Query Log der MySQL.
Hoppla: Da haben wir eine Query, welche 12 Sekunden dauert und auf 6 Tabellen zugreift. Diese Tabellen sind somit während dieser Zeit gelockt.
INSERTs und
UPDATEs müssen warten. Die auslösenden PHP-Prozesse auf den Webservern hängen, belegen einen Apache-Slot und damit Speicher. Mit der Zeit sind die all 512 Slots belegt, der Speicher gefüllt und der Benutzer hat seine Seite immer noch nicht gesehen.
Not nice.
Die Query (stark vereinfacht):
[source:sql]
SELECT name
FROM persons
INNER JOIN regions ON persons.region_id=regions.region_id
WHERE persons.region_id=5 OR regions.country_id=1
[/source]
“OR-Alarm”!
“OR-Alarm”? Das ist, wenn im
WHERE-Statement einer Query ein
OR benutzt wird. Und dieses
OR verbindet zwei unterschiedliche Felder. Diese beiden Felder können zwar Indexiert sein, aber der Index kann wegen em
OR nicht verwendet werden. Andere
WHERE-Bedingungen, welche die Verwendung einens Indexes zulassen würden, sind nicht vorhanden. Dies kann man mit dem
EXPLAIN SELECT-Kommando leicht überprüfen.
Wenn der Index nicht verwendet werden kann, muss die
WHERE-Condition für alle Kombinationen von
persons und
regions überprüft werden. Bei 50’000 Personen und 100 Regionen sind das 5’000’000 Kombinationen, welche sequenziell durchaufen werden müssen. Das Ergebnis ist klar: Die Abfrage dauert lange 12 Sekunden.
Lässt man die eine Bedingung fallen, verschwindet das
OR und der Index auf
region_id kann verwendet werden:
SELECT name
FROM persons
INNER JOIN regions ON persons.region_id=regions.region_id
WHERE regions. country_id =1
[/source]
Ueberprüfung mit EXPLAIN SELECT: Kein kartesisches Produkt mehr notwendig. Kein Table Scan mehr. Nur noch 10ms. Das gleiche gilt, wenn man die andere Bedingung fallen lässt.
Aufgeteilt mehr Leistung
Nun könnte man im Client diese beiden Queries hintereinander ausführen lassen und die resultierenden Zeilen in einem Array sammeln. Ergibt 20ms Ausführungszeit. Aber wenig eleganten Code.
Schöner ist die
UNION-Clause: Hier kann man die Ergebnisse von mehreren Queries vereinigen.
SELECT name
FROM persons
INNER JOIN regions ON persons.region_id=regions.region_id
WHERE regions. country_id =1
UNION
SELECT name
FROM persons
WHERE persons.region_id=5
[/source]
Intern werden nur beide Teilqueries separat ausgeführt. Da die Teilqueries kein
OR mehr besitzen, kann der Index verwendet werden. Beide Teilqueries sind somit rasend schnell.
Resultat: Das gleiche wie bei der Query mit dem OR. Ausführungszeit 1000x höher.