Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning

De dagar dÄ man inte behövde oroa sig för databasens prestanda Àr förbi. Tiden stÄr inte still. Varje ny teknologientreprenör vill skapa nÀsta Facebook och samla in all data de kan fÄ tag pÄ. Denna data behövs för att företag ska kunna trÀna modeller av högre kvalitet som hjÀlper dem att tjÀna pengar. Under dessa förhÄllanden mÄste programmerare skapa API:er som gör det möjligt att snabbt och pÄlitligt arbeta med enorma datamÀngder.

Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning

Om du har arbetat med att designa serverdelar av applikationer eller databaser under en tid har du förmodligen skrivit kod för att utföra paginering i dina frÄgor. Till exempel sÄ hÀr:

SELECT * FROM table_name LIMIT 10 OFFSET 40

Är det sĂ„?

Men om du har utfört paginering pÄ detta sÀtt sÄ mÄste jag tyvÀrr pÄpeka att du inte har gjort det pÄ det mest effektiva sÀttet.

Vill du invÀnda mot mig? Du kan inte slösa tid. Slack, Shopify och Mixmax anvÀnder redan de metoder som jag vill berÀtta om idag.

NÀmn en enda backendutvecklare som aldrig har anvÀnt OFFSET och LIMIT för att utföra paginerade frÄgor. I ett MVP (Minimum Viable Product, minimi livskraftig produkt) eller i projekt dÀr smÄ datamÀngder anvÀnds Àr detta tillvÀgagÄngssÀtt fullt tillÀmpbart. Det 'bara fungerar', sÄ att sÀga.

Men om man behöver skapa pÄlitliga och effektiva system frÄn grunden, bör man se till att man i förvÀg tar hÀnsyn till effektiviteten i frÄgor mot databaser som anvÀnds i sÄdana system.

Idag ska vi diskutera problem relaterade till de allmÀnt anvÀnda (tyvÀrr) implementeringarna av pagineringsmekanismer och hur man kan uppnÄ hög prestanda vid utförandet av sÄdana frÄgor.

Vad Àr fel med OFFSET och LIMIT?

Som tidigare nÀmnts OFFSET och LIMIT presterar utmÀrkt i projekt dÀr man inte behöver arbeta med stora datamÀngder.

Problemet uppstÄr nÀr databasen vÀxer till en storlek som gör att den inte lÀngre fÄr plats i serverns minne. Men under arbetet med denna databas mÄste man anvÀnda paginerade frÄgor.

För att detta problem ska framtrÀda krÀvs det en situation dÀr databashanteraren (DBMS) anvÀnder en ineffektiv fullstÀndig tabellskanning (Full Table Scan) för att utföra varje frÄga med paginering (under tiden kan det ske operationer för att infoga och ta bort data, och förÄldrad data Àr inte relevant för oss!).

Vad Àr "fullstÀndig tabellskanning" (eller "sekventiell tabellgenomgÄng", Sequential Scan)? Det Àr en operation dÀr DBMS sekventiellt lÀser varje rad i tabellen, det vill sÀga de data som finns i den, och kontrollerar dem mot ett specificerat villkor. Det Àr vÀlkÀnt att denna typ av tabellgenomgÄng Àr den lÄngsammaste. Problemet Àr att det vid denna operation förekommer mÄnga in-/utdataoperationer som belastar serverns diskunderhÄll. Situationer förvÀrras av förseningar kopplade till arbetet med data lagrade pÄ diskar, och det faktum att överföring av data frÄn disk till minne Àr en resurskrÀvande operation.

Till exempel, om du har poster om 100000000 anvÀndare och du utför en frÄga med konstruktionen OFFSET 50000000. Detta betyder att DBMS mÄste ladda alla dessa poster (och vi behöver dem inte ens!), placera dem i minnet, och först dÀrefter hÀmta, sÀg, 20 resultat som anges i LIMIT.

LÄt oss sÀga att det kan se ut sÄ hÀr: «vÀlj rader frÄn 50000 till 50020 av 100000». Det vill sÀga, för att utföra frÄgan mÄste systemet först ladda 50000 rader. FörstÄr du hur mycket onödigt arbete det mÄste utföra?

Om du inte tror mig - ta en titt pÄ exemplet som jag skapade med hjÀlp av db-fiddle.com. 

Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning
Exempel pÄ db-fiddle.com

DÀr, till vÀnster, i fÀltet Schema SQL, finns det kod som infogar 100000 rader i databasen, och till höger, i fÀltet Query SQL, visas tvÄ frÄgor. Den första, som Àr lÄngsam, ser ut sÄ hÀr:

SELECT *
FROM `docs`
LIMIT 10 OFFSET 85000;

Och den andra, som Àr en effektiv lösning pÄ samma problem, ser ut sÄ hÀr:

SELECT *
FROM `docs`
WHERE id > 85000
LIMIT 10;

För att utföra dessa frÄgor rÀcker det att trycka pÄ knappen Run i den övre delen av sidan. Genom att göra detta kan vi jÀmföra information om svarstider. Det visar sig att det tar minst 30 gÄnger lÀngre tid att utföra en ineffektiv förfrÄgan Àn en andra (frÄn körning till körning varierar denna tid, till exempel kan systemet rapportera att den första förfrÄgningen tog 37 ms, medan den andra tog 1 ms).

Och om datamĂ€ngden ökar, kommer allt att se Ă€nnu vĂ€rre ut (för att övertyga dig — kolla pĂ„ min exempel med 10 miljoner rader).

Det vi just diskuterade bör ge dig en viss förstÄelse för hur frÄgor till databaser faktiskt behandlas.

TĂ€nk pĂ„ att ju högre vĂ€rde OFFSET — desto lĂ€ngre tid tar det att köra förfrĂ„gan.

Vad bör man anvÀnda istÀllet för kombinationen OFFSET och LIMIT?

IstÀllet för kombinationen OFFSET och LIMIT bör man anvÀnda en konstruktion som Àr uppbyggd enligt följande schema:

SELECT * FROM table_name WHERE id > 10 LIMIT 20

Detta Àr en förfrÄgan med sidindelning, baserad pÄ en kursorn (Cursor based pagination).

IstÀllet för att lokalt lagra aktuella OFFSET och LIMIT och skicka dem med varje förfrÄgan, bör man lagra den senaste erhÄllna primÀra nyckeln (vanligtvis Àr det ID) och LIMIT, vilket resulterar i förfrÄgningar som liknar den ovanstÄende.

Varför? Faktum Àr att genom att tydligt ange identifieraren för den sista lÀsta raden, informerar du din DBMS om var den ska börja söka efter de nödvÀndiga uppgifterna. Dessutom kommer sökningen, tack vare anvÀndningen av nyckeln, att ske effektivt, och systemet behöver inte avbryta för att titta pÄ rader utanför det angivna intervallet.

LÄt oss titta pÄ följande jÀmförelse av prestandan för olika förfrÄgningar. HÀr Àr en ineffektiv förfrÄgan.

Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning
LÄngsam förfrÄgan

Och hÀr Àr en optimerad version av denna förfrÄgan.

Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning
En snabb förfrÄgan

BÄda förfrÄgningarna returnerar exakt samma mÀngd data. Men den första tar 12,80 sekunder att köra, medan den andra tar 0,01 sekund. KÀnner du skillnaden?

Möjliga problem

För att sÀkerstÀlla att den föreslagna metoden för att utföra förfrÄgningar fungerar effektivt, mÄste tabellen innehÄlla en eller flera kolumner med unika, sekventiellt ordnade index, som en heltalsidentifierare. I vissa specifika fall kan detta avgöra framgÄngen för anvÀndningen av sÄdana förfrÄgningar för att öka hastigheten pÄ databasanvÀndningen.

Naturligtvis mÄste man ta hÀnsyn till tabellernas arkitektur nÀr man konstruerar förfrÄgningar och vÀlja de mekanismer som bÀst presterar med de aktuella tabellerna. Till exempel, om man behöver arbeta med stora mÀngder relaterade data i förfrÄgningar, kan det vara intressant för dig. detta artikel.

Om vi har problemet med avsaknad av en primÀrnyckel, till exempel om det finns en tabell med en 'mÄnga-till-mÄnga'-relation, sÄ Àr den traditionella metoden som förutsÀtter anvÀndning av OFFSET och LIMIT, vi garanterat kommer att kunna tillÀmpa. Men dess anvÀndning kan leda till att potentiellt lÄngsamma förfrÄgningar utförs. I sÄdana fall skulle jag rekommendera att anvÀnda en primÀrnyckel med autoinkrement, Àven om den endast behövs för att organisera förfrÄgningar med paginering.

Om du Ă€r intresserad av detta Ă€mne — hĂ€r, hĂ€r och hĂ€r — hĂ€r Ă€r nĂ„gra anvĂ€ndbara resurser.

Sammanfattning

Det huvudsakliga slutsatsen vi kan dra Àr att oavsett storleken pÄ databaserna mÄste förfrÄgningarnas hastighet alltid analyseras. I vÄr tid Àr skalbarhet av lösningar extremt viktigt, och om man redan frÄn början av arbetet med ett system designar allt korrekt, kan detta i framtiden spara utvecklaren frÄn mÄnga problem.

Hur analyserar och optimerar du förfrÄgningar mot databaser?

Det Àr inte bra att anvÀnda OFFSET och LIMIT i förfrÄgningar med sidindelning

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster