sql >> Database >  >> RDS >> Database

Onbedoelde bijwerkingen - Slaapsessies die sloten vasthouden

Een recente adviesopdracht was gericht op het blokkeren van problemen binnen SQL Server die vertragingen veroorzaakten bij het verwerken van gebruikersverzoeken van de toepassing. Toen we ons begonnen te verdiepen in de problemen die werden ondervonden, werd het duidelijk dat vanuit het oogpunt van SQL Server het probleem draaide om sessies in een slapende status die sloten in de engine vasthielden. Dit is geen typisch gedrag voor SQL Server, dus mijn eerste gedachte was dat er een soort van applicatie-ontwerpfout was waardoor een transactie actief was op een sessie die was gereset voor het poolen van verbindingen in de applicatie, maar dit werd al snel bewezen dat dit niet het geval was omdat de sluizen later automatisch werden vrijgegeven, was er alleen vertraging in dit optreden. Dus moesten we verder graven.

Sessiestatus begrijpen

Afhankelijk van naar welke DMV u kijkt voor SQL Server, kan een sessie een aantal verschillende statussen hebben. Een slaapstatus betekent dat de engine de opdracht heeft voltooid, dat alles tussen client en server interactief is voltooid en dat de verbinding wacht op de volgende opdracht van de client. Als de slaapsessie een openstaande transactie heeft, is deze altijd gerelateerd aan code en niet aan SQL Server. De opengehouden transactie kan door een aantal dingen worden verklaard. De eerste mogelijkheid is een procedure met een expliciete transactie die de XACT_ABORT-instelling niet inschakelt en vervolgens een time-out geeft zonder dat de applicatie het opschonen correct afhandelt, zoals uitgelegd in dit echt oude bericht door het CSS-team:

  • Hoe het werkt:wat is een slapende / wachtende commandosessie

Als de procedure de instelling XACT_ABORT had ingeschakeld, zou het de transactie automatisch hebben afgebroken toen deze een time-out had en zou de transactie zijn teruggedraaid. SQL Server doet precies wat het moet doen onder ANSI-normen en om de ACID-eigenschappen van de uitgevoerde opdracht te behouden. De time-out is niet gerelateerd aan SQL Server, maar wordt ingesteld door de .NET-client en de eigenschap CommandTimeout, dus ook dat is codegerelateerd en niet gerelateerd aan SQL Engine. Dit is hetzelfde soort probleem waar ik het ook over had in mijn Extended Events-serie, in deze blogpost:

  • Meerdere doelen gebruiken om verweesde transacties te debuggen

In dit geval gebruikte de toepassing echter geen opgeslagen procedures voor toegang tot de database en werd alle code gegenereerd door een ORM. Op dit punt verschoof het onderzoek van SQL Server en meer naar hoe de applicatie de ORM gebruikte en waar transacties zouden worden gegenereerd door de codebasis van de applicatie.

Inzicht in .NET-transacties

Het is algemeen bekend dat SQL Server elke wijziging van gegevens in een transactie verpakt die automatisch wordt doorgevoerd, tenzij de IMPLICIT_TRANSACTIONS-setoptie is ingeschakeld voor een sessie. Na te hebben geverifieerd dat dit voor geen enkel deel van hun code AAN was, was het redelijk veilig om aan te nemen dat alle transacties die achterbleven nadat een sessie Sleeping was, het resultaat waren van een expliciete transactie die ergens tijdens de uitvoering van hun code werd geopend. Nu was het gewoon een kwestie van begrijpen wanneer, waar en vooral waarom het niet onmiddellijk werd afgesloten. Dit leidt tot een van de weinige verschillende scenario's waarnaar we moesten zoeken in hun applicatielaagcode:

  • De applicatie die een TransactionScope() gebruikt rond een operatie
  • De applicatie die een SqlTransaction() inschakelt op de verbinding
  • De ORM-code die bepaalde aanroepen in een interne transactie verpakt die niet is vastgelegd

De documentatie voor TransactionScope sloot dat vrij snel uit als mogelijke oorzaak hiervan. Als u het transactiebereik niet voltooit, wordt het automatisch teruggedraaid en wordt de transactie afgebroken wanneer het wordt verwijderd, dus het is niet erg waarschijnlijk dat dit aanhoudt bij het opnieuw instellen van de verbinding. Evenzo wordt het SqlTransaction-object automatisch teruggedraaid als het niet is vastgelegd wanneer de verbinding wordt gereset voor pooling van verbindingen, zodat dit al snel een niet-starter voor het probleem werd. Dit verliet net de ORM-codegeneratie, althans dat dacht ik, en het zou ongelooflijk vreemd zijn voor een oudere versie van een veel voorkomende ORM om dit soort gedrag te vertonen, naar mijn ervaring, dus we moesten verder graven.

De documentatie voor de ORM die ze gebruiken, stelt duidelijk dat wanneer een actie van meerdere entiteiten plaatsvindt, deze binnen een transactie wordt uitgevoerd. Multi-entiteitsacties kunnen recursieve opslagen zijn of het opslaan van een entiteitverzameling terug naar de database vanuit de applicatie, en de ontwikkelaars waren het erover eens dat dit soort bewerkingen overal in hun code plaatsvinden, dus ja, de ORM moet transacties gebruiken, maar waarom waren ze ineens een probleem wordt.

De kern van het probleem

Op dit punt deden we een stap terug en begonnen we een holistische beoordeling van de hele omgeving uit te voeren met behulp van New Relic en andere monitoringtools die beschikbaar waren toen de blokkeringsproblemen opdoken. Het begon duidelijk te worden dat de slaapsessies met vergrendelingen alleen plaatsvonden wanneer de IIS-toepassingsservers onder extreme CPU-belasting stonden, maar dat was op zichzelf niet genoeg om de vertraging te verklaren die werd waargenomen bij het vrijgeven van vergrendelingen door transactiecommits. Het bleek ook dat de applicatieservers virtuele machines waren die draaiden op een overbelaste hypervisor-host, en de CPU Ready-wachttijden voor hen waren ernstig verhoogd ten tijde van de blokkeringsproblemen op basis van de sommatiewaarden die door de VM-beheerder werden verstrekt.

De Sleeping-status zal plaatsvinden met een open transactie die vergrendelingen vasthoudt tussen de .SaveEntity-aanroepen van de voltooiing van de objecten en de definitieve vastlegging in de code gegenereerde code erachter voor de objecten. Als de VM/App-server onder druk of belasting staat, kan dit vertraging oplopen en tot blokkeringsproblemen leiden, maar het probleem zit niet in SQL Server, het doet precies wat het zou moeten doen binnen de reikwijdte van de transactie. Het probleem is uiteindelijk het resultaat van de vertraging bij het verwerken van het commit-punt aan de applicatiezijde. Het verkrijgen van de timing van het statement en RPC-complete events van Extended Events samen met de database_transaction_end event timing toont de round-trip vertraging van de app-laag die de transactie op de open verbinding afsluit. In dit geval is alles wat gezien wordt in SQL Server het slachtoffer van een overbelaste applicatieserver en een overbelaste VM-host. Het verplaatsen/verdelen van de applicatiebelasting over servers in een NLB- of hardware load-balanced configuratie met behulp van hosts die niet te veel toegewijd zijn aan CPU-gebruik, zou de onmiddellijke vastlegging van de transacties snel herstellen en de Sleeping-sessies met vergrendelingen in SQL Server verwijderen.

Nog een ander voorbeeld van een milieuprobleem dat veroorzaakte wat leek op een alledaags blokkeringsprobleem. Het loont altijd om te onderzoeken waarom de blokkerende thread zijn vergrendelingen niet snel kan ontgrendelen.


  1. DateTime converteren naar YYYY-MM-DD-formaat in SQL Server

  2. FOUT:toestemming geweigerd voor relatietabelnaam op Postgres tijdens het proberen van een SELECT als een alleen-lezen gebruiker

  3. Stappen om stand-by te synchroniseren met primaire database in Oracle

  4. PostgreSQL in opkomst:Postgres-bevindingen 2018 en trends voor 2019