Az adatbázis teljesítmény javítása általában azzal kezdődik, hogy valaki nagyobb példányt javasol, és általában azzal ér véget, hogy kiderül: egyetlen lekérdezés minden oldalbetöltéskor szekvenciális olvasást végzett 4 millió soron. A hardver soha nem volt a korlát. A végrehajtási terv volt az.
Ez a minta elég következetesen ismétlődik ahhoz, hogy érdemes legyen alapfeltevésként kimondani. Ha egy alkalmazás lassú, az adatbázis pedig dolgozik, az ok szinte mindig néhány konkrét lekérdezés, nem pedig az általános kapacitáshiány. A nagyobb gép pontosan addig fedi el a problémát, amíg a tábla újra meg nem nő.
Mielőtt bármit módosítanál, mérj. Egy megérzés alapján kiválasztott lekérdezés optimalizálása az a mód, ahogyan a csapatok egy hetet töltenek olyan indexek felvételével, amelyek lassítják az írásokat, az olvasásokat pedig egyáltalán nem gyorsítják. Minden adatbázis meg tudja mondani, mely utasítások fogyasztják a legtöbb összesített időt. Ott kezdd, javítsd ki a legrosszabbat, majd mérj újra. Két vagy három ilyen kör általában lezárja az incidenst.
Az adatbázis teljesítmény a lekérdezés megtalálásával kezdődik
Az összesített idő többet számít, mint a legrosszabb eset. Egy lekérdezés, amely két másodpercig tart és naponta kétszer fut, lényegtelen. Egy lekérdezés, amely negyven ezredmásodpercig tart és percenként 8000-szer fut, az igazi problémád, és egy egy másodperces küszöbbel beállított lassú lekérdezési naplóban soha nem fog megjelenni.
Postgresben a pg_stat_statements bővítmény pontosan ezt összegzi: hívásszám, összesített idő és átlagos idő normalizált utasításonként. Rendezd összesített idő szerint, és a vétkes általában az első három sor között lesz. A MySQL hasonló összesítést kínál a performance schema segítségével.
Két dolgot érdemes ellenőrizni, mielőtt magát a lekérdezést hibáztatnád. Minden alkalommal lassú, vagy csak bizonyos órákban, ami inkább erőforrás-versengésre utal, mint a tervre? És önmagában is lassú, vagy csak párhuzamos terhelés alatt, ami zárolásokra vagy kapcsolatkorlátra utal?
Olvasd el a tervet ahelyett, hogy találgatnál
Ha megvan az utasítás, kérdezd meg az adatbázistól, hogyan szándékozik lefuttatni. A Postgres ezt az EXPLAIN paranccsal mutatja meg
, és a fontos változat az EXPLAIN ANALYZE, amely ténylegesen lefuttatja a lekérdezést, és becslések helyett valódi időket jelent.
Ebből a kimenetből három dolog hordozza a jelzés nagy részét.
Szekvenciális olvasás egy nagy táblán. Az adatbázis minden sort beolvas. Kis táblán ez helyes és gyors. Nagy táblán azt jelenti, hogy az általad írt feltételhez nincs használható index, vagy hogy a tervező úgy döntött, az index használata nem éri meg.
Nagy eltérés a becsült és a tényleges sorszám között. A tervező statisztikákból választ stratégiát, így amikor a becslése nagyságrendekkel téved, olyan okokból dönt rosszul, amelyeknek semmi köze a lekérdezésedhez. Az elavult statisztika gyakori és könnyen javítható ok.
Egyetlen csomópontra összpontosuló idő. A tervek fák, és a javítás oda tartozik, amelyik csomópont az időt elfogyasztotta. Bármi más optimalizálása semmit nem változtat.
Az az ösztön, hogy azonnal indexet vegyél fel, amint szekvenciális olvasást látsz, gyakran helyes, mégis megér harminc másodperc önuralmat, mert az ok, amiért az index nem használódik, olykor többet nyom a latban, mint a hiánya.
Miért nem segítenek az indexek
Egy létező index még nem olyan index, amelyet ténylegesen használnak is.
A feltétel nem sargable. Ha egy oszlopot függvénybe csomagolsz, vagy számolsz vele, az általában megakadályozza az adott oszlopon lévő index használatát, mert az index az oszlop értékeit tárolja, nem az átalakítottakat. A feltétel újraírása úgy, hogy az oszlop érintetlen maradjon, rendszerint visszaállítja a használatot.
Rossz az oszlopsorrend az összetett indexben. Az összetett index azokat a lekérdezéseket szolgálja ki, amelyek a vezető oszlopait használják. Egy előbb az egyik, majd a másik oszlopra épülő index nem segít azon a lekérdezésen, amely csak a másodikra szűr, és ebbe folyamatosan bele lehet futni.
A tervező olcsóbbnak tartja a teljes olvasást. Ha egy lekérdezés a tábla nagy hányadát adja vissza, a szekvenciális olvasás valóban gyorsabb, mint az indexen keresztüli ugrálás. Ez helyes viselkedés, és a javítás az, hogy kevesebbet adj vissza.
Elavultak a statisztikák. Tömeges betöltés vagy nagy törlés után a tervező adatokról alkotott képe súlyosan hibás lehet, amíg a statisztikákat fel nem frissítik.
És minden index kerül valamibe. Az írásoknak karban kell tartaniuk, és olyan memóriát foglal, amely egyébként adatokat gyorsítótárazna. Egy tizenöt indexszel rendelkező táblában általában több olyan is akad, amelyre senkinek nincs szüksége, és mindegyik lassítja az összes beszúrást.
Az N+1 probléma máig a legnagyobb egyedi ok
Az alkalmazások lassúsága többször ered ebből, mint bármilyen tervezési gondból, és soha nem lassú lekérdezésként jelentkezik, mert minden egyes lekérdezés gyors.
Az alakzat ismerős. Lekérsz egy 100 rekordból álló listát, majd végigmész rajta, és mindegyikhez lekéred a kapcsolódó adatokat. Az eredmény 101 oda-vissza fordulat ott, ahol egy vagy kettő is elég lenne. Minden lekérdezés három ezredmásodperc alatt tér vissza, az oldal mégis fél másodpercig tart, mert a költség a hálózati fordulókban van, nem a munkában.
Az objektumrelációs leképezők könnyen írhatóvá teszik ezt véletlenül, mert a kapcsolódó adat elérése tulajdonságolvasásnak látszik, nem adatbázishívásnak. A javítás az, hogy a kapcsolódó adatokat egyetlen lekérdezésben, a szülőhalmazzal együtt töltöd be, amit minden érett ORM támogat, és amit a legtöbb alapértelmezés szerint mégsem tesz meg.
Az észlelése egyszerű: számold meg a kérésenkénti lekérdezéseket. Ha egy oldal a megjelenített elemek számával arányos mennyiségű lekérdezést indít, megtaláltad. Ez egyben a legnagyobb hasznot hozó ellenőrzés, amikor egy alkalmazás a peremhálózaton lassú, ahogyan azt a Cloudflare Hyperdrive útmutatónk is leírja, mert a hálózati fordulók sokkal drágábbak, ha nagyobb a távolság.
Kapcsolatok és versengés
Két probléma, amely lassúságnak látszik, pedig nem az.
A kapcsolatok kimerülése. Minden adatbázisnak van felső korlátja az egyidejű kapcsolatokra, és mindegyik memóriába kerül. Ha egy alkalmazás többet nyit, mint amennyit a készlet enged, a kérések sorban állnak egy szabad kapcsolatra várva, és az alkalmazás lassúnak tűnik, miközben az adatbázis tétlenül áll. A tünet magas alkalmazásoldali késleltetés alacsony adatbázis-processzorterhelés mellett, a javítás pedig a kapcsolatkészlet használata, nem a nagyobb gép.
Zárolási versengés. Egy hosszú tranzakció, amely zárolást tart, mindent blokkol, ami mögötte várakozik. A szokásos ok egy olyan tranzakció, amely nyitva marad olyan munka alatt, amelyhez nincs is szükség az adatbázisra, például egy másik szolgáltatás felé indított HTTP-hívás alatt. Tartsd a tranzakciókat rövidnek, és szorítsd őket magára az adatbázison végzett munkára.
Mindkét esetet érdemes korán kizárni, mert mindkettőt könnyű lekérdezési problémának félreolvasni, és egyiket sem oldja meg egy index.
Mit tegyél, és milyen sorrendben
Keresd meg azokat az utasításokat, amelyek a legtöbb összesített időt fogyasztják. Futtass EXPLAIN ANALYZE parancsot a legrosszabbra, és olvasd el, hová megy el valójában az idő. Ellenőrizd a kérésenkénti lekérdezésszámot, hogy kizárd az N+1 esetét, mielőtt bármit optimalizálnál. Frissítsd a statisztikákat, mielőtt indexet vennél fel, mert néha ennyi az egész javítás. Ezután vedd fel a legszűkebb indexet, amely kiszolgálja a feltételt, és mérj újra.
A Mecanik ezt a szoftverfejlesztési munkánk részeként végzi, és az eredmény szinte mindig ugyanaz: két vagy három lekérdezés volt a felelős, a javítás kicsi volt, és a nagyobb példányra, amelyet senki nem vett meg, soha nem volt szükség.
Kapcsolódó bejegyzések: API verziózás: mikor törj kompatibilitást és hogyan ne , Hogyan fejlesszünk webalkalmazást 2026-ban , Jelszavak tárolása: mit használj 2026-ban , Egyedi szoftverfejlesztés az Egyesült Királyságban .
Gyakran ismételt kérdések
Hogyan találom meg, melyik lekérdezés lassítja az alkalmazásomat? Összesített idő szerint rendezz, ne a legrosszabb eset szerint. Egy negyven ezredmásodperces lekérdezés, amely percenként 8000-szer fut, sokkal többe kerül, mint egy két másodperces, amely naponta kétszer fut, és egy egy másodperces küszöbbel beállított lassú lekérdezési naplóban soha nem jelenik meg. Postgresben a pg_stat_statements utasításonként összesíti a hívásokat és az összesített időt, a MySQL pedig ugyanezt a performance schema révén kínálja.
Mit keressek az EXPLAIN ANALYZE kimenetében? Három dolog hordozza a jelzés nagy részét: szekvenciális olvasás egy nagy táblán, vagyis nincs használható index; nagy eltérés a becsült és a tényleges sorszám között, vagyis a tervező rossz statisztikákból dolgozik; és egyetlen tervcsomópontra összpontosuló idő, mert a javítás oda tartozik. Bármely másik csomópont optimalizálása semmit nem változtat.
Miért nem használja a rendszer az indexemet? Általában négy ok egyike miatt. A feltétel függvénybe csomagolja az oszlopot vagy számol vele, így az index már nem illeszkedik. Az összetett index oszlopai olyan sorrendben állnak, amely nem szolgálja ki a lekérdezést. A lekérdezés a tábla akkora hányadát adja vissza, hogy a teljes olvasás valóban olcsóbb. Vagy a statisztikák elavultak egy tömeges betöltés vagy törlés után.
Mi az N+1 lekérdezési probléma? Lekérsz egy rekordlistát, majd mindegyikhez külön lekérdezést indítasz a kapcsolódó adatokért, ami 101 oda-vissza fordulatot eredményez ott, ahol egy vagy kettő is elég lenne. Egyetlen lassú lekérdezési naplóban sem jelenik meg, mert minden lekérdezés gyors; a költség a hálózati fordulókban van. Úgy észleled, hogy megszámolod a kérésenkénti lekérdezéseket, és a megjelenített elemekkel arányos számot keresel.
Megoldja egy nagyobb adatbázis-kiszolgáló a lassú lekérdezéseket? Ritkán, és csak átmenetileg. Ha egy alkalmazás lassú, az adatbázis pedig dolgozik, az ok szinte mindig néhány konkrét lekérdezés, nem pedig a kapacitás hiánya, így a nagyobb gép addig fedi el a problémát, amíg a tábla újra meg nem nő. A magas alkalmazásoldali késleltetés alacsony adatbázis-processzorterhelés mellett általában kimerült kapcsolatkészletre utal, amin a több hardver nem segít.
Hozzászólások