Adott egy viszonylag komplex rendszer (jelen esetben az aprOmega 2), amelyből a Drupal Backup and Migrate moduljával, annak alapértelmezett beállításai mellett készítesz adatbázismentést. Azt gondolnád, hogy egy ilyen adatbázismentés visszatöltésekor az adatbázisod olyan állapotba kerül, hogy a közepesen bonyolult, 10 táblát érintő lekérdezésed futásideje legalább nagyságrendileg megegyezik az eredeti állapottal? Nos, bizonyos esetekben tévedsz.
A teljes igazsághoz hozzátartozik az is, hogy nem ugyanazon a hardveren töltöttem vissza az adatbázist, mint amelyiken elmentettem: éles szerverként nem a fejlesztői gépet szoktuk használni (már csak azért sem, mert ha lefelejtesz bizonyos táblákról bizonyos indexeket, akkor egy hasonló bonyolultságú lekérdezésnél a MySQL könnyen elszáll válaszidőben annyira, hogy inkább újraindítod - amit ugye éles szerveren inkább nem játszik meg az ember, ha nem muszáj). A lekérdezést előállító PHP kód és programverziók viszont (természetesen) ugyanazok, mint az éles szerveren.
A jelenség: ugyanazon kód ugyanazon verziójú programokkal és frissen visszatöltött adatbázison való futtatása esetén az éles rendszeren elfogadható (néhány másodperces) a válaszidő (beleértve a PHP feldolgozást és a böngésző renderelést is, azaz a felhasználói válaszidőt tekintve), míg fejlesztői gépen kivárhatatlanul hosszú - miközben a szerver és fejlesztői gép hardverfelépítésének különbsége ezt nem indokolja.
Nem tudom, a nálam tapasztaltabbak ilyenkor mit tesznek először: én a lekérdezést próbáltam optimalizálni, a teljes, szűrőkkel és rendezésekkel ellátott SELECT elé beírva egy EXPLAIN-t - hátha kifelejtettem valami indexet. „Természetesen” nem ez volt a probléma - s így utólag bevallom, butaság is volt ez irányba indulni, hiszen akkor a szerveren is kivárhatatlan válaszidőnek kellett volna lennie. Pedig a megoldáshoz vezető útra a MySQL Optimizing Queries with EXPLAIN fejezetében leltem meg a választ (egy viszonylag egyszerű példa magyarázatában):
At this point, the query is optimized almost as well as possible. The remaining problem is that, by default, MySQL assumes that values in the tt.ActualPC column are evenly distributed, and that is not the case for the tt table. Fortunately, it is easy to tell MySQL to analyze the key distribution:
mysql> ANALYZE TABLE tt;
„Kicsit” feljebb (abban a részben, amit az ember átugrik, amikor a „lényeget” keresi) a következőt is megtalálhatjuk:
If you have a problem with indexes not being used when you believe that they should be, you should run ANALYZE TABLE to update table statistics such as cardinality of keys, that can affect the choices the optimizer makes. See Section 12.5.2.1, “ANALYZE TABLE Syntax”.
A „meglepetés”: az érintett táblákra lefuttatva ezt az <a href="http://www.imdb.com/title/tt0289848/">ANALYZE <var>THA</var>T</a>ABLE
lekérdezést, a fejlesztői gépen is elfogadható válaszidőt kapok. S hogy ez miért meglepetés?
Az adatbázismentés ugyebár (lényegében) DROP TABLE ...; CREATE TABLE ...; INSERT INTO ...;
sorozatokból áll - azaz az adatbázis vonatkozó tábláját teljesen törli, újra létrehozza, majd feltölti adatokkal. Csakhogy e visszatöltési folyamat során a mélyen tisztelt MySQL nem gyűjti össze azt az információt, amely a későbbi lekérdezések optimális futtatásához szükséges, pedig szerintem meg kellene tennie - mint ahogy állítólag a PostgreSQL meg is teszi, egy háttérben futó folyamattal.
Öröm az ürömben, hogy az ANALYZE TABLE
futásideje a tényleges lekérdezés futásidejéhez képest elhanyagolható, így adódik a workaround: a hasonlóan bonyolult lekérdezések előtt inkább futtassuk le minden alkalommal, mintsem akár egyszer is a felhasználó szedje le a szenteltvizet a helpdeskesről... Örömmel venném, ha valaki elfogadhatóan meg tudná indokolni, miért nem szedi össze a MySQL a kulcsok eloszlásáról szóló adatokat az adatbázismentés visszatöltésekor, miért nem jelentkezik ez a probléma az éles szerveren levő eredeti adatbázisban, illetve van-e erre a problémára az említett workaroundnál szebb megoldás.