Waarom een praatgrage SQL-app over een VPN kruipt en wat je moet batchen
Een scherm dat honderd afhankelijke query's afvuurt, betaalt honderd keer de round trip. Tel de wachttijden, batch de onafhankelijke, en verplaats daarna de uitvoering.
Een formulier dat op een LAN in minder dan een seconde opent, doet er over een VPN vijf seconden over, terwijl een bestandskopie op dezelfde lijn op volle snelheid loopt. Het verschil is geen bandbreedte. Het formulier wacht op een reeks antwoorden en betaalt bij elke stap de volledige round trip, terwijl de bestandskopie bytes continu in beweging houdt. Tel die wachttijden voordat je de accesslijn aanraakt.
Het symptoom is een scherm, niet een query
Elke query op dat scherm kan snel zijn en het scherm kan toch traag zijn, omdat het totaal de som van wachttijden is en niet de som van uitvoeringstijden. Een statement dat in twee milliseconden loopt, kost nog steeds een volledige round trip wanneer de client het resultaat moet ontvangen voordat hij het volgende verstuurt. Op een pad met veertig milliseconden latentie tellen honderd afhankelijke aanroepen op tot ongeveer vier seconden voordat er enig renderwerk gebeurt. Deze rekenkunde is een model van het wachten, geen benchmark van jouw lijn.
Daarom ziet een snelheidstest er onschuldig uit. Bulkoverdracht meet doorvoer, een praatgraag formulier meet latentie, en een VPN kan in het ene uitstekend zijn en in het andere gewoon blijven. Het scherm diagnosticeren betekent dus het scherm meten en niet de lijn.
Noteer de end-to-end tijd van één gebruikersactie voordat je iets verandert. Dat getal is de enige basislijn die kan vertellen of een latere fix werkte.
Tel sequentiële wachttijden in één gebruikersactie
Instrumenteer de client in plaats van te gissen. In psql drukt \timing on de verstreken tijd van elk statement af, en EXPLAIN (ANALYZE, BUFFERS) laat zien hoeveel van die tijd de server werkelijk aan het werk besteedde.
Het gat tussen die twee getallen is je netwerk- en clientoverhead. Als een statement in twee milliseconden wordt uitgevoerd en de client registreert zestig, dan zijn de resterende achtenvijftig reis en lokale verwerking, en geen enkele index haalt die weg. Herhaal die vergelijking voor elk statement dat het scherm uitstuurt en tel de gaten op in plaats van de uitvoeringstijden.
Schrijf het resultaat op als een aantal sequentiële wachttijden. Dat is de grootheid die de volgende twee secties kunnen verkleinen, en het is een getal waarover je met bewijs kunt discussiëren.
Batch de onafhankelijke aanroepen, houd de afhankelijke
Twee statements die elkaars resultaat niet lezen, kunnen samen reizen en één keer worden opgehaald, wat twee wachttijden in één verandert. Twee statements waarbij de tweede de waarde van de eerste nodig heeft, kunnen dat niet, omdat de afhankelijkheid echt is en batchen zou veranderen wat het programma doet. Sorteer de aanroepen in die twee stapels voordat je iets herschrijft, en de grootte van elke stapel vertelt je wat mogelijk is.
Setgebaseerd werk verwijdert wachttijden grondiger dan batchen. Eén query die de rijen teruggeeft die een lus één voor één ophaalde, elimineert de lus en de round trips. Zoek eerst de lus, want die is meestal de grootste winst en de makkelijkst te beoordelen wijziging.
Een batch moet nog steeds foutafhandeling en transactiegrenzen behouden. Het samenvoegen van ongerelateerde statements tot één string is geen veilige optimalisatie, en een batch die een mislukt statement achter een geslaagde inzending verbergt, is erger dan de wachttijden die hij bespaarde.
Hergebruik verbindingen voordat je bandbreedte koopt
Een nieuwe verbinding kost naamresolutie, een TCP-handshake, TLS-onderhandeling en authenticatie, en op een pad met hoge latentie is elk daarvan nog een round trip voordat de eerste query loopt. Connection pools bestaan om die kosten één keer per proces te betalen in plaats van één keer per aanroep.
In ADO.NET geeft het sluiten of disposen van een gepoolde verbinding hem terug voor hergebruik in plaats van een nieuwe fysieke verbinding te openen, dus het patroon dat op een lek lijkt is vaak het juiste gebruik. Controleer of de pool echt in gebruik is in plaats van het aan te nemen: log het aanmaken van fysieke verbindingen, of kijk naar het sessieaantal op de server terwijl het scherm draait. De SQL-prestatierichtlijnen zeggen hetzelfde over het verminderen van round trips en het batchen van frequente ad-hocaanroepen, met dienstspecifieke voorbeelden die je op je eigen implementatie moet toepassen.
Pooling haalt de opzetkosten weg en laat de seriële querywachttijden onaangeroerd. Het is een voorwaarde voor een schone meting, geen oplossing op zichzelf.
Weeg af of je de uitvoering dichter bij de data kunt brengen
De applicatie dicht bij de database draaien vervangt veel externe database-uitwisselingen door één externe weergavesessie. Voor een praatgrage werklast kan dat een grote verandering zijn, en het is net zo goed een operationele verandering als een prestatieverandering: sessiebeheer, toegangsbeheer, software-uitrol en gebruikerservaring bewegen allemaal mee met de applicatie.
Meet de werkelijke interface en de data die hij overdraagt voordat je aanneemt dat een richting sneller is. Een remote-desktopsessie verplaatst pixels, en een scherm met dichte graphics kan over hetzelfde pad slechter presteren dan de query's die het verving, vooral wanneer de client continu opnieuw tekent.
Herhaal dan de oorspronkelijke actie en vergelijk drie getallen: sequentiële wachttijden, resultaatvolume en end-to-end voltooiingstijd. Een verbetering die de gemeten gebruikerstaak verandert terwijl de datacorrectheid en het toegangsbeleid behouden blijven, is degene die je moet houden.