For at fortsætte min serie af artikler om låse, vil jeg denne gang diskutere APPEND_ONLY_STORAGE_INSERT_POINT låsen og vise, hvordan den kan være en stor flaskehals for tunge opdateringsarbejdsbelastninger, hvor begge former for snapshot-isolering bliver brugt.
Jeg anbefaler stærkt, at du læser det første indlæg i serien før dette, så du har al den generelle baggrundsviden om låse.
Hvad er APPEND_ONLY_STORAGE_INSERT_POINT-låsen?
For at forklare denne lås, er jeg nødt til at forklare lidt om, hvordan snapshot-isolering fungerer.
Når du aktiverer en af de to former for versionering, bruger SQL Server en mekanisme kaldet versionering for at bevare versioner før ændringer af en post i version store i tempdb. Dette gøres på følgende måde:
- En post er identificeret som lige ved at blive ændret.
- Den aktuelle post kopieres til versionslagret.
- Posten er ændret.
- Hvis posten ikke allerede havde et 14-byte versionstag , tilføjes en til slutningen af posten. Tagget indeholder et tidsstempel (ikke en realtid) og en pointer til den tidligere version af posten i versionslagret.
- Hvis posten allerede havde et versioneringstag, opdateres den med det nye tidsstempel og versionslagermarkør.
Forekomstomfattende versioneringstidsstemplet øges, når en ny sætning eller batch begynder, eller der oprettes en ny version af en post, i enhver database, hvor begge former for snapshot-isolering er aktiveret. Dette tidsstempel bruges til at sikre, at en forespørgsel behandler den korrekte version af en post.
Forestil dig f.eks. en database, der havde læst committed snapshot aktiveret, så hver erklæring er garanteret at se posterne fra det tidspunkt, hvor erklæringen startede. Versionstidsstemplet er indstillet til, hvornår sætningen startede, så enhver post, den støder på, der har et højere tidsstempel, er den "forkerte" version, og derfor skal den "rigtige" version, med et tidsstempel før sætningens tidsstempel, hentes fra version butik. Mekanikken i denne proces er ikke relevant i forbindelse med dette indlæg.
Så hvordan opbevares versionerne fysisk i versionslagret? Hele posten før ændringen, inklusive kolonner uden for rækken, kopieres til versionslagret, opdelt i 8.000-byte bidder, som kan strække sig over to sider, hvis det er nødvendigt (f.eks. 2.000 bytes i slutningen af en side og 6.000 bytes ved starten på det næste). Dette lager til særlige formål består af tildelingsenheder, der kun kan tilføjes og bruges kun til version store operationer. Det kaldes det, fordi nye data kun kan tilføjes umiddelbart efter slutningen af den senest indtastede version. En ny allokeringsenhed oprettes med jævne mellemrum, og dette gør det muligt at rydde op i almindelig versionsbutik meget effektivt - da en unødvendig tildelingsenhed simpelthen kan droppes. Igen, mekanikken i det er uden for rammerne af dette indlæg.
Og nu kommer vi til definitionen af låsen:enhver tråd, der skal kopiere en pre-change record til versionslagret, skal vide, hvor indsættelsespunktet er i den aktuelle append-only allokeringsenhed. Disse oplysninger er beskyttet af APPEND_ONLY_STORAGE_INSERT_POINT-låsen.
Hvordan bliver låsen en flaskehals?
Her er problemet:Der er kun én acceptabel tilstand, hvor APPEND_ONLY_STORAGE_INSERT_POINT-låsen kan erhverves:EX-tilstand (eksklusiv). Og som du ved fra at læse introindlægget til serien, kan kun én tråd ad gangen holde låsen i EX-tilstand.
Ved at samle alle disse oplysninger:Når en eller flere databaser har snapshot-isolering aktiveret, og der er en høj nok samtidig arbejdsbelastning af opdateringer til disse databaser, vil der være en masse versioner, der bliver genereret af de forskellige forbindelser, og denne lås bliver en lidt af en flaskehals, hvor størrelsen på flaskehalsen øges, efterhånden som opdateringsbelastningen øges, hvor versionsstyring er involveret.
Viser flaskehalsen
Du kan nemt genskabe flaskehalsen for dig selv. Jeg gjorde det som følger:
- Oprettet en tabel med en masse heltalskolonner ved navn cXXX, hvor XXX er et tal og et klynget indeks på en int-identitetskolonne ved navn DocID
- Indsatte 100.000 poster med tilfældige værdier for alle kolonnerne
- Oprettet et script med en uendelig løkke for at vælge et tilfældigt DocID i intervallet 1 til 10.000, vælg et tilfældigt kolonnenavn, og øg kolonneværdien med 1 (derved oprettes en version)
- Oprettede ni identiske scripts, men hver valgte fra et forskelligt 10.000 værdi klynge nøgleområde
- Indstil DELAYED_DURABILITY til FORCED for at reducere ventetiden på WRITELOG (du ville ganske vist sjældent gøre dette, men det hjælper med at forværre flaskehalsen til demoformål)
Jeg kørte derefter alle ti scripts samtidigt og målte Access Methods:Index Searches/sek-tælleren for at spore, hvor mange opdateringer der fandt sted. Jeg kunne ikke bruge Databaser:Batch-anmodninger/sek., da hvert script kun havde én batch (den uendelige løkke), og jeg ønskede ikke at bruge Transaktioner/sek., da det kunne tælle interne transaktioner såvel som den, der indpakkede hver opdatering.
Da snapshot-isolering ikke var aktiveret, på min Windows 10 bærbare computer, der kører SQL Server 2019, fik jeg omkring 80.000 opdateringer i sekundet på tværs af de ti forbindelser. Da jeg derefter satte indstillingen READ_COMMMITED_SNAPSHOT til ON for databasen og kørte testen igen, faldt arbejdsbyrden til omkring 60.000 opdateringer pr. sekund (et 25 % fald i gennemløbet). Fra at se på ventestatistikker var 85 % af alle ventetider LATCH_EX, og fra at se på låsestatistikker var 100 % for APPEND_ONLY_STORAGE_INSERT_POINT.
Husk på, at jeg satte scenariet op for at vise flaskehalsen, når den er værst. I et virkeligt miljø med en blandet arbejdsbyrde er den generelt accepterede vejledning for et gennemstrømningsfald ved brug af snapshot-isolering 10-15 %.
Oversigt
Et andet potentielt område, der kan blive påvirket af denne flaskehals, er tilgængelighedsgruppelæsbare sekundære. Hvis en databasereplika er indstillet til at være læsbar, bruger alle forespørgsler mod den automatisk snapshot-isolering, og al genafspilning af logposter fra den primære vil generere versioner. Med en høj nok opdateringsbelastning, der kommer fra den primære og mange databaser indstillet til at være læsbare, og hvor parallel redo er normen for tilgængelighedsgruppesekundære, kan APPEND_ONLY_STORAGE_INSERT_POINT-låsen muligvis også blive en flaskehals på en tilgængelighedsgruppe, der kan læses, hvilket kan føre til sekundært falder bagud det primære. Jeg har ikke testet dette, men det er nøjagtig den samme mekanisme, som jeg har beskrevet ovenfor, så det virker sandsynligt. I så fald er det muligt at deaktivere parallel redo ved hjælp af sporingsflag 3459, men dette kan føre til en dårligere samlet gennemstrømning på den sekundære.
Hvis man lægger scenariet for tilgængelighedsgruppe til side, er det desværre den eneste måde at undgå denne flaskehals fuldstændigt, hvilket ikke er en levedygtig mulighed, hvis din arbejdsbyrde er afhængig af semantikken fra snapshot-isolering, eller hvis du har brug for det for at afhjælpe blokeringsproblemer (da snapshot-isolering betyder, at læseforespørgsler ikke erhverver aktielåse, som blokerer forespørgsler om ændringer).
Rediger:fra kommentarerne nedenfor, *kan* du fjerne låseflaskehalsen ved at bruge ADR i SQL Server 2019, men så er ydeevnen meget dårligere på grund af ADR-overhead. Scenariet, hvor låsen bliver en flaskehals på grund af den høje opdateringsbelastning, er absolut ikke en gyldig use case for ADR.