Optimalisering av spørringer
GraphQL-spørringer kan ha varierende ytelse, avhengig av hvor store de er, og hvor godt databasen er optimalisert for den enkelte spørringen. Hvis en spørring går tregt, er det flere ting vi kan gjøre. Her finner du tips til hvordan du kan jobbe med trege spørringer.
Hva er for tregt
Hva som er for tregt, avhenger av behovet spørringen skal dekke. En spørring som skal gjøres direkte fra et brukergrensesnitt, bør ikke ta mer enn 500 millisekunder. En spørring som gjøres av en maskinbruker som skal tanke mye data i en nattjobb, kan ta lengre tid uten at det skaper problemer for brukeren. SIS-subgrafen er satt opp med en timeout på 30 sekunder for GraphQL-spørringer. En spørring som tar lengre tid enn dette, vil feile med en 504 Gateway timeout for brukere som går direkte mot subgrafen, og med en generell GraphQL-feil for brukere som går via supergrafen.
Identifisere trege spørringer
Vi kan jobbe proaktivt med å identifisere spørringer som går tregt. For FS-Admin går det varslinger når det oppstår feil i produksjonsmiljøet til denne slack-kanalen: # studieadm-alerts-fsadmin. Denne vil varsle om spørringer som er så trege at de timer ut. Denne spørringen i Grafana henter ut GraphQL-spørringer som tar mer enn 30 sekunder i demo-miljøet. Du kan tilpasse spørringen for andre miljøer, eller du kan . I Grafana-spørringen over kan du klikke deg inn på hver enkelt av de trege spørringene og se hvor mye tid som går med i hvilket teknologi lag, og også hvilke konkrete databasespørringer som er trege.
Finn spørreplanen i databasen
Når databasen mottar en spørring, vil den forsøke å finne den mest optimale måten å svare på spørringen på. Vi kan be databasen om å vise oss spørreplanen den har laget for en gitt spørring. Vi kan gjøre følgende spørring i databasen der den trege spørringen dukket opp:
select sql_id, child_number from v$sql where sql_text like '%"alias_23918271"."FEIDEID_DOMENE"%';
Her har vi hentet ut en del av SQL-spørringen som vi fant i Grafana i forrige punkt, som vi antar er forholdsvis unik.
Det gir oss følgende resultat:
SQL_ID CHILD_NUMBER
________________ _______________
bw5cs5udb5ph3 0
bw5cs5udb5ph3 1
bw5cs5udb5ph3 6
Her har Oracle laget flere alternative spørreplaner for samme spørring (samme SQL_ID). Forå finne den sist brukte, kan vi gjøre følgende spørring:
SELECT sql_id, child_number, last_active_time, executions
FROM v$sql
WHERE sql_id = 'bw5cs5udb5ph3'
ORDER BY last_active_time DESC;
Så kan vi gjøre følgende spørring for å finne selve spørreplanen:
SELECT * FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
sql_id => 'bw5cs5udb5ph3',
cursor_child_no => 6,
format => 'ALLSTATS LAST'
)
);
Hent spørreplan direkte
Når du skal jobbe med å forbedre spørringen, er det ofte greit å hente ut spørreplanen direkte. Dette må gjøres i samme database som du feilsøker, og med samme bruker og roller som den trege spørringen kjøres med.
For brukere med legacy-tilgangsstyring (fs_wsrole-rollen), må du logge inn med Ixxx_FS_WS-brukeren.
For brukere med RAS-tilgangsstyring, må du logge inn med RAS_SESSION_MANAGER, og sette riktige roller, f.eks. slik:
DECLARE
session_id VARCHAR2(32);
BEGIN
fsapi_v2a.pk_ras.create_and_attach(
p_Institusjonsnr => '184',
p_Applikasjonnavn => 'maskinbruker',
p_Applikasjonsroller => fsapi_v2a.pk_ras.t_rolleliste('studentdata_les1'),
p_Fodselsnr => NULL,
p_SudoFodselsnr => NULL,
p_SessionId => session_id
);
DBMS_OUTPUT.PUT_LINE('Session ID: ' || session_id);
END;
Den faktiske SQLen APIet kjører, kan du finne i Wildfly-terminal-loggen, ved å kjøre samme spørring som brukeren i ditt lokale utviklingsmiljø.
For å kunne kjøre samme spørring mot en annen database, gjør du følgende for å sikre at den går til kjerne-API-skjemaet:
ALTER SESSION SET CURRENT_SCHEMA = FSAPI_V2A;
Deretter kan du kjøre SQL-spørringen som du fant i loggen, med EXPLAIN PLAN FOR foran.
Deretter (i samme sesjon):
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Da får du ut spørreplanen.
Nå kan du gjøre større eller mindre tilpasninger i spørringen.
Kjør spørringen direkte for å se hvor lang tid den tar, kjør EXPLAIN PLAN FOR igjen, og hent ut spørreplanen på nytt for å se om den har endret seg.
Jeg har en spørreplan, hva nå?
Når du har hentet ut spørreplanen, kan du analysere den for å finne flaskehalser. Dette er litt komplisert, og her kan du gjerne be om hjelp fra en databaseutvikler, eller bruke KI. Her er noen tips:
- Se etter "full table scans" (FTS) i planen.
- Sjekk om indekser blir brukt.
- Sjekk om RAS-policies blir evaluert på et tidlig tidspunkt i planen (vi ønsker å ha disse så sent som mulig, fordi de har en kostnad for hver rad). RAS-policies med column constraints er ekstra kostbare, da de utføres både for hver rad og hvor hver kolonne med constraint på seg.
Hva kan jeg gjøre for å forbedre ytelsen?
Det er flere ting du kan gjøre for å forbedre ytelsen til en spørring.
Skriv manuelle conditions for filtrering
En filtrert spørring kan være ineffektiv dersom filteret ikke treffer indekser i databasen. Vi kan overstyre SQL-en som genereres av Graphitron ved å skrive en manuell condition og mappe den opp i GraphQL-skjemaet. Dette er ofte en god strategi der feltet vi ønsker å filtrere på, er en syntetisk kolonne i jOOQ, da disse sjelden blir optimalisert for filtrering. I disse tilfellene er det ofte bedre å skrive en condition som filtrerer direkte på de underliggende kolonnene som den syntetiske kolonnen beregnes fra.
Del opp spørringen med @splitQuery
Spørringene som genereres fra GraphQL-skjemaet kan bli store og komplekse, med mange left-joins.
Det kan ofte være bedre å dele opp spørringen i flere mindre spørringer.
Vi kan gjøre dette ved å bruke @splitQuery-direktivet på felt i GraphQL-skjemaet der left-joinen gjøres.
Gjøre om left joins til inner joins
Grunnen til at vi automatisk genererer left joins, er at det ikke er sikkert at brukeren har tilgang til alle dataene.
Hvis vi vet at brukeren alltid har tilgang til dataene (f.eks. der den refererte tabellen er tilgjengelig for alle brukere med STUDIEELEMENTER_LES1-rollen, og vi ikke tror det vil endres), kan vi gjøre om left joins til inner joins.
Dette kan vi i noen tilfeller løse ved å skrive en manuell condition som overstyrer det som genereres av Graphitron.
Dette kan også være en nyttig strategi i kalkulerte felter innført med syntetiske kolonner i jOOQ eller med @externalField i GraphQL-skjemaet.
Omgå column constraints med views
Hvis en tabell har column constraints, og du vil bruke en av kolonnene med constraint i en join eller til filtrering, er det ganske vanskelig å få til en effektiv spørreplan. I slike tilfeller kan det være lurt å lage et view som ikke har column constraints (men som har samme regler for tilgjengelighet som column constrainten), og bruke dette i stedet. Dette gir oss imidlertid ekstra kompleksitet på databasesiden (vi ender fort med dedikerte "lese"- og "skrive"-views med en slik strategi), så det bør kun gjøres når det er nødvendig.
Tilgangsstyringsproblemer
Vi gjør tilgangsstyring med Oracle RAS, noe som av og til kan påvirke ytelsen negativt. Ett kjent problem oppsto da vi la en Column Constraint på en kolonne som inngikk i primærnøkkelen til tabellen. Det gjorde spørringer mot tabellen veldig trege, også for brukere som hadde tilgang til kolonnen. Column constraints har også en kostnad i seg selv. Jo flere kolonner i en tabell som har constraints, jo flere sjekker må gjøres for hver rad som leses.
Optimalisere databasen
I noen tilfeller kan det lønne seg å legge på ekstra indekser for å gjøre spørringen raskere. Snakk i så fall med en databaseutvikler om dette.
Alle databaser er forskjellige
Oracle bruker mange forskjellige verktøy for å optimalisere spørringer, og det som fungerer i én database, fungerer ikke nødvendigvis i en annen. En spørring mot en tabell med en million rader i produksjon, vil antakelig få hen helt annen spørreplan enn samme spørring i lokalt utviklingsmiljø der det kun er noen tusen rader i tabellen. Når du jobber med optimalisering av spørringer er det derfor viktig at du tester mot samme database som tregheten oppleves i. Når du har oppnådd en ytelse du er fornøyd med, er det ofte lurt å test i andre miljøer også, for eventuelt å avdekke problemer som kun oppstår i andre databaser.