sql >> Database teknologi >  >> RDS >> Database

Utilsigtede bivirkninger - Sovende sessioner, der holder låse

Et nyligt konsulentengagement var fokuseret på at blokere problemer inde i SQL Server, der forårsagede forsinkelser i behandlingen af ​​brugeranmodninger fra applikationen. Da vi begyndte at grave i de problemer, der opleves, blev det klart, at fra et SQL Server-synspunkt drejede problemet sig om sessioner i en Sovende-status, der holdt låse inde i motoren. Dette er ikke en typisk adfærd for SQL Server, så min første tanke var, at der var en form for applikationsdesignfejl, der efterlod en transaktion aktiv på en session, der var blevet nulstillet til forbindelsespooling i applikationen, men dette blev hurtigt bevist, at ikke for at være tilfældet, da låsene senere blev udløst automatisk, var der blot en forsinkelse i dette. Så vi var nødt til at grave videre.

Forstå sessionsstatus

Afhængigt af hvilken DMV du ser på for SQL Server, kan en session have et par forskellige statusser. En Sleeping-status betyder, at motoren har fuldført kommandoen, alt mellem klient og server er gennemført interaktionsmæssigt, og forbindelsen venter på, at den næste kommando kommer fra klienten. Hvis den sovende session har en åben transaktion, er den altid relateret til kode og ikke SQL Server. Transaktionen, der holdes åben, kan forklares med et par ting. Den første mulighed er en procedure med en eksplicit transaktion, der ikke aktiverer XACT_ABORT-indstillingen og derefter timeout, uden at applikationen håndterer oprydningen korrekt som forklaret i dette virkelig gamle indlæg af CSS-teamet:

  • Sådan virker det:Hvad er en sovende/afventende kommandosession

Hvis proceduren havde aktiveret XACT_ABORT-indstillingen, ville den have afbrudt transaktionen automatisk, når den udløb, og transaktionen ville være blevet rullet tilbage. SQL Server gør præcis, hvad den er påkrævet for at gøre under ANSI-standarder og for at opretholde ACID-egenskaberne for den kommando, der blev udført. Timeoutet er ikke SQL Server-relateret, det indstilles af .NET-klienten og CommandTimeout-egenskaben, så det er også koderelateret og ikke SQL Engine-relateret adfærd. Dette er den samme slags problem, som jeg også talte om i min serie med udvidede begivenheder, i dette blogindlæg:

  • Brug af flere mål til at fejlsøge forældreløse transaktioner

Men i dette tilfælde brugte applikationen ikke lagrede procedurer til adgang til databasen, og al koden blev genereret af en ORM. På dette tidspunkt skiftede undersøgelsen væk fra SQL Server og mere mod, hvordan applikationen brugte ORM, og hvor transaktioner ville blive genereret af applikationskodebasen.

Forstå .NET-transaktioner

Det er almen viden, at SQL Server ombryder enhver dataændring i en transaktion, der automatisk bliver forpligtet, medmindre indstillingen IMPLICIT_TRANSACTIONS er TIL for en session. Efter at have bekræftet, at dette ikke var ON for nogen del af deres kode, var det ret sikkert at antage, at alle transaktioner, der var tilbage efter en session var i søvn, var resultatet af en eksplicit transaktion, der blev åbnet et sted under udførelsen af ​​deres kode. Nu var det bare et spørgsmål om at forstå, hvornår, hvor og vigtigst af alt, hvorfor det ikke blev lukket ud med det samme. Dette fører til et af nogle få forskellige scenarier, som vi skulle kigge efter inde i deres programlagkode:

  • Applikationen bruger en TransactionScope() omkring en operation
  • Applikationen, der registrerer en SqlTransaction() på forbindelsen
  • ORM-koden, der ombryder visse opkald i en transaktion internt, som ikke bliver forpligtet

Dokumentationen for TransactionScope udelukkede ret hurtigt, at det var en mulig årsag til dette. Hvis du undlader at fuldføre transaktionsomfanget, vil den automatisk rulle tilbage og afbryde transaktionen, når den bortskaffes, så det er ikke særlig sandsynligt, at dette vil fortsætte på tværs af forbindelsesnulstillinger. Ligeledes vil SqlTransaction-objektet automatisk rulle tilbage, hvis det ikke er committet, når forbindelsen nulstilles til forbindelsespooling, så det blev hurtigt en ikke-starter for problemet. Dette forlod lige ORM-kodegenereringen, det var i hvert fald, hvad jeg troede, og det ville være utroligt mærkeligt for en ældre version af en meget almindelig ORM at udvise denne type adfærd fra min erfaring, så vi var nødt til at grave videre.

Dokumentationen for den ORM, de bruger, angiver klart, at når en handling med flere enheder opstår, udføres den inde i en transaktion. Multi-entity handlinger kunne være rekursive lagringer eller at gemme en enhedssamling tilbage til databasen fra applikationen, og udviklerne var enige om, at disse typer operationer sker over hele deres kode, så ja, ORM'en skal bruge transaktioner, men hvorfor var de lige pludselig bliver et problem.

Roden til problemet

På dette tidspunkt tog vi et skridt tilbage og begyndte at lave en holistisk gennemgang af hele miljøet ved at bruge New Relic og andre overvågningsværktøjer, der var tilgængelige, da blokeringsproblemerne dukkede op. Det begyndte at blive klart, at de sovende sessioner med låse kun fandt sted, når IIS-applikationsserverne var under ekstrem CPU-belastning, men det i sig selv var ikke nok til at tage højde for den forsinkelse, der blev set i transaktions-commits, der frigav låse. Det viste sig også, at applikationsserverne var virtuelle maskiner, der kørte på en overengageret hypervisor-vært, og de CPU-klare ventetider for dem var alvorligt forhøjede på tidspunktet for blokeringsproblemerne baseret på summeringsværdierne leveret af VM-administratoren.

Sovende status vil forekomme med en åben transaktion, der holder låse mellem .SaveEntity-kaldene for objekterne, der fuldføres, og den endelige commit i den kodegenererede kode bag for objekterne. Hvis VM/App-serveren er under pres eller belastning, kan dette blive forsinket og føre til problemer med blokering, men problemet er ikke i SQL Server, den gør præcis, hvad den skal inden for transaktionens omfang. Problemet er i sidste ende resultatet af forsinkelsen i behandlingen af ​​forpligtelsespunktet på ansøgningssiden. At få timingen for erklæringen afsluttet og RPC fuldførte hændelser fra Extended Events sammen med database_transaction_end hændelsestidspunktet viser tur-retur-forsinkelsen fra app-niveauet, der lukker transaktionen på den åbne forbindelse. I dette tilfælde er alt, der ses i SQL Server, offer for en overbelastet applikationsserver og en overbelastet VM-vært. Flytning/opdeling af applikationsbelastningen på tværs af servere i en NLB- eller hardwarebelastningsbalanceret konfiguration ved brug af værter, der ikke er overbelagte på CPU-brug, ville hurtigt genoprette den umiddelbare commit af transaktionerne og fjerne de sovende sessioner, der holder låse i SQL Server.

Endnu et eksempel på et miljøproblem, der forårsager, hvad der lignede et løbe-af-the-mill blokeringsproblem. Det kan altid betale sig at undersøge, hvorfor den blokerende tråd ikke er i stand til at frigøre sine låse hurtigt.


  1. Gruppér efter klausul i mySQL og postgreSQL, hvorfor fejlen i postgreSQL?

  2. Hvordan ændrer jeg datoformatet i Postgres?

  3. SQL Server - stop eller bryd udførelse af et SQL-script

  4. SQL udvælger rækker efter seneste dato med to unikke kolonner