Att skapa en tabell är en relativt resurskrävande och tidskrävande operation. Servern måste lokalisera och allokera lagringsutrymme för de nya data- och indexstrukturerna och göra motsvarande poster i flera systemmetadatatabeller. Allt detta arbete måste göras på ett sätt som alltid kommer att fungera korrekt under hög samtidighet, och som uppfyller alla ACID-garantier som förväntas av en relationsdatabas.
I SQL Server innebär detta att man tar rätt typer av lås och spärrar, i rätt ordning, samtidigt som man säkerställer att detaljerade transaktionsloggposter är säkert engagerade i beständig lagring före eventuella fysiska ändringar i databasen. Dessa loggposter säkerställer att systemet kan återställa databasen till ett konsekvent tillstånd i händelse av en återställning av transaktioner eller systemkrasch.
Att släppa ett bord är en lika dyr operation. Lyckligtvis skapar eller släpper de flesta databaser inte tabeller med någon stor frekvens. Det uppenbara undantaget från detta är systemdatabasen tempdb . Denna enda databas innehåller fysisk lagring, allokeringsstrukturer, systemmetadata och transaktionsloggposter för alla temporära tabeller och tabellvariabler över hela SQL Server-instansen.
Det ligger i karaktären av temporära tabeller och tabellvariabler att skapas och släppas mycket oftare än andra databasobjekttyper. När denna naturligt höga frekvens av skapande och förstörelse kombineras med den koncentrerade effekten av att alla temporära tabeller och tabellvariabler associeras med en enda databas, är det knappast förvånande att konflikter kan uppstå i allokerings- och metadatastrukturerna för tempdb databas.
Tillfällig objektcache
För att minska effekten på tempdb strukturer, kan SQL Server cache tillfälliga objekt för återanvändning. Istället för att ta bort ett tillfälligt objekt behåller SQL Server systemets metadata och trunkerar tabelldata. Om tabellen är 8MB eller mindre utförs trunkeringen synkront; annars används uppskjuten droppe. I båda fallen minskar trunkeringen lagringskravet till en enda (tom) datasida och allokeringsinformationen till en enda IAM-sida.
Cachning undviker nästan alla allokerings- och metadatakostnader för att skapa det temporära objektet nästa gång. Som en bieffekt av att göra färre ändringar i tempdb databas än en fullständig släpp- och återskapningscykel, minskar temporär objektcachelagring också mängden transaktionsloggning som krävs.
Uppnå cachning
Tabellvariabler och lokala temporära tabeller kan båda cachelagras. För att kvalificera sig för cachelagring måste en lokal temporär tabell eller tabellvariabel måste skapas i en modul:
- Lagrad procedur (inklusive en tillfälligt lagrad procedur)
- Utlösare
- Tabellvärderad funktion med flera påståenden
- Skalär användardefinierad funktion
Returvärdet för en tabellvärderad funktion med flera påståenden är en tabellvariabel som själv kan cachelagras. Tabellvärdade parametrar (som också är tabellvariabler) kan cachelagras när parametern skickas från en klientapplikation, till exempel i .NET-kod med SqlDbType.Structured
. När satsen parametriseras kan tabellvärdade parameterstrukturer endast cachelagras på SQL Server 2012 eller senare.
Följande kan inte cachelagras:
- Globala tillfälliga tabeller
- Objekt skapade med ad-hoc SQL
- Objekt skapade med dynamisk SQL (t.ex. med
EXECUTE
ellersys.sp_executesql
)
För att cachelagras måste ett tillfälligt objekt dessutom inte :
- Har namngivna begränsningar (begränsningar utan explicita namn är helt okej)
- Utför "DDL" efter att objektet har skapats
- Var i en modul definierad med
WITH RECOMPILE
alternativ - Bli anropad med
WITH RECOMPILE
alternativet förEXECUTE
uttalande
För att uttryckligen ta itu med några vanliga missuppfattningar:
TRUNCATE TABLE
inte förhindra cachningDROP TABLE
inte förhindra cachningUPDATE STATISTICS
inte förhindra cachning- Automatiskt skapande av statistik gör inte förhindra cachning
- Manuell
CREATE STATISTICS
kommer förhindra cachning
Alla tillfälliga objekt i en modul bedöms för cachningslämplighet separat. En modul som innehåller ett eller flera temporära objekt som inte kan cachelagras kan fortfarande kvalificera sig för cachelagring av andra temporära objekt inom samma modul.
Ett vanligt mönster som inaktiverar cachning för temporära tabeller är skapandet av index efter den initiala tabellskapandesatsen. I de flesta fall kan detta lösas med hjälp av primärnyckel och unika begränsningar. I SQL Server 2014 och senare har vi möjlighet att lägga till icke-unika icke-klustrade index direkt i tabellskapandesatsen med hjälp av INDEX
klausul.
Övervakning och underhåll
Vi kan se hur många temporära objekt som för närvarande är cachade med hjälp av cache-räknare DMV:
VÄLJ DOMCC.[typ], DOMCC.pages_kb, DOMCC.pages_in_use_kb, DOMCC.entries_count, DOMCC.entries_in_use_countFROM sys.dm_os_memory_cache_counters AS DOMCC WHERE DOMCC.[name] =Njutbara Temporarys'Ett exempelresultat är:
En cachepost anses vara använd så länge som någon del av den innehållande modulen körs. Samtidiga körningar av samma modul kommer att resultera i att flera cachade temporära objekt skapas. Flera exekveringsplaner för samma modul (kanske på grund av olika session
SET
alternativ) kommer också att leda till flera cacheposter för samma modul.Cacheposter kan åldras över tiden som svar på konkurrerande behov av minne. Cachade temporära objekt kan också tas bort (asynkront, av en bakgrundssystemtråd) när den överordnade modulens exekveringsplan tas bort från planens cache.
Även om det inte stöds (eller på något sätt rekommenderas) för produktionssystem, kan det tillfälliga objektcachelagret manuellt rensas helt för teständamål med:
DBCC FREESYSTEMCACHE('Temporära tabeller och tabellvariabler') MED MARK_IN_USE_FOR_REMOVAL;WAITFOR DELAY '00:00:05';Fördröjningen på fem sekunder ger tid för bakgrundsrensningsuppgiften att köras. Observera att det här kommandot är faktiskt farligt . Du bör endast använda det (på egen risk) på en testinstans som du har exklusiv tillgång till. När du har testat klart, starta om SQL Server-instansen.
Cachingimplementeringsdetaljer
Tabellvariabler implementeras av en "riktig" användartabell i tempdb databas (men inte en tabell som vi kan fråga direkt). Namnet på den associerade tabellen är "#" följt av den åttasiffriga hexadecimala representationen av objekt-id. Följande fråga visar förhållandet:
-- En tabellvariabelDECLARE @Z AS-tabell (z heltal NULL); -- Motsvarande sys.tables entrySELECT T.[name], ObjIDFromName =CONVERT(heltal, CONVERT(binär(4), RIGHT(T.[namn], 8), 2)), T.[object_id], T.[ type_desc], T.create_date, T.modify_dateFROM tempdb.sys.tables SOM T WHERE T.[namn] SOM N'#[0-9A-F][0-9A-F][0-9A-F][0 -9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]';Ett exempelresultat visas nedan. Lägg märke till hur objekt-id:t beräknat från objektnamnet matchar det faktiska objekt-id:
Att köra det skriptet som ad-hoc SQL kommer att producera en annan tempdb objekt-ID (och objektnamn) vid varje exekvering (ingen cachning). Om du placerar samma skript inuti en modul (t.ex. en lagrad procedur) kan tabellvariabeln cachelagras (så länge dynamisk SQL inte används), så att objektets ID och namn blir samma vid varje körning.
När tabellvariabeln inte cachelagras skapas och släpps den underliggande tabellen varje gång. När tillfällig objektcache är aktiverad, trunkeras tabellen i slutet av modulen istället för att släppas. Det finns inga ändringar till systemmetadata när en tabellvariabel cachelagras. Effekten på allokeringsstrukturer och transaktionsloggning är begränsad till att radera raderna i tabellen och ta bort överflödig data och allokeringssidor när modulen avslutas.
Tillfälliga tabeller
När en temporär tabell används istället för en tabellvariabel är den grundläggande mekanismen i huvudsak densamma, med bara ett par extra byte av namn:När en temporär tabell är inte cachad , det är synligt i tempdb med det välbekanta användarnamnet, följt av ett gäng understreck och den hexadecimala representationen av objekt-id:t som ett sista suffix. Den lokala temporära tabellen finns kvar tills den explicit tas bort eller tills omfattningen där den skapades slutar. För ad-hoc SQL betyder detta när sessionen kopplas från servern.
För en cachad temporär tabell , första gången modulen körs skapas den temporära tabellen precis som för det icke-cachelagrade fallet. I slutet av modulen, istället för att släppas automatiskt (eftersom omfattningen där den skapades slutar), trunkeras den temporära tabellen och döps sedan om till den hexadecimala representationen av objektets ID (exakt som för tabellvariabeln). Nästa gång modulen körs byts den cachade tabellen om från det hexadecimala formatet till det användarnamn som anges (plus understreck plus hex-objekt-id).
De extra namnbyteoperationerna i början och slutet av modulen involverar ett litet antal metadataändringar av systemet. . Cachade temporära tabeller kan därför fortfarande uppleva åtminstone viss metadatastrid under mycket höga återanvändningsfrekvenser. Ändå är metadataeffekten av en cachad temporär tabell mycket lägre än för det icke-cachelagrade fallet (skapar och släpper tabellen varje gång).
Mer detaljer och exempel på hur tillfällig objektcache fungerar finns i min tidigare artikel.
Statistik över cachade temporära tabeller
Som nämnts tidigare kan statistik vara automatiskt skapas på temporära tabeller utan att förlora fördelarna med tillfällig objektcache (som en påminnelse, att manuellt skapa statistik kommer inaktivera cachning).
En viktig varning är att statistiken associerade med en cachad temporär tabell återställs inte när objektet cachelagras i slutet av modulen, eller när det cachade objektet hämtas från cachen i början av modulen. Som en konsekvens kan statistik på en cachad temporär tabell bli över från en icke-relaterad tidigare exekvering. Med andra ord kan statistiken ha absolut ingen relation till det aktuella innehållet i den tillfälliga tabellen.
Detta är uppenbarligen oönskat, med tanke på att huvudskälet att föredra en lokal temporär tabell framför en tabellvariabel är tillgången på korrekt distributionsstatistik. Som begränsning kommer statistiken att uppdateras automatiskt när (om) det ackumulerade antalet ändringar av det underliggande cachade objektet når den interna omkompileringströskeln. Detta är svårt att bedöma i förväg, eftersom detaljerna är komplexa och något kontraintuitiva.
Den mest omfattande lösningen, samtidigt som man behåller fördelarna med tillfällig objektcache, är att:
- Manuellt
UPDATE STATISTICS
på det tillfälliga bordet i modulen; och - Lägg till ett
OPTION (RECOMPILE)
ledtråd till uttalanden som refererar till den temporära tabellen
Naturligtvis är det en kostnad inblandad i att göra detta, men detta är oftast acceptabelt. I själva verket, genom att välja att använda en lokal temporär tabell i första hand, säger modulförfattaren implicit att planval sannolikt är känsligt för innehållet i den temporära tabellen, så omkompilering kan vara meningsfullt. Att uppdatera statistiken manuellt säkerställer att statistiken som används under omkompileringen återspeglar det aktuella innehållet i tabellen (som vi säkert förväntar oss).
För mer information om exakt hur detta fungerar, se min tidigare artikel om ämnet.
Sammanfattning och rekommendationer
Tillfällig objektcachelagring inom en modul kan avsevärt minska trycket på delad allokering och metadatastrukturer i tempdb databas. Den största minskningen kommer att ske när du använder tabellvariabler eftersom cachelagring och återanvändning av dessa temporära objekt inte involverar modifiering av metadata alls (inga byte av namn). Konflikt om allokeringsstrukturer kan fortfarande uppstå om den enstaka cachade datasidan inte räcker för att hålla alla tabellvariablers data vid körning.
Inverkan på planens kvalitet på grund av bristen på kardinalitetsinformation för tabellvariabler kan mildras genom att använda OPTION(RECOMPILE)
eller spårningsflagga 2453 (tillgänglig från SQL Server 2012 och framåt). Observera att dessa begränsningar endast ger optimeraren information om det totala antalet rader i tabellen.
För att generalisera, tabellvariabler används bäst när informationen är liten (helst passar in på en enda datasida för maximala fördelar med konflikter) och när planvalet inte beror på värdena som finns i tabellvariabeln.
Om information om datadistributionen (densitet och histogram) är viktigt för planval, använd en lokal tillfällig tabell istället. Se till att uppfylla villkoren för temporär tabellcache, vilket oftast innebär att du inte skapar index eller statistik efter den initiala tabellskapandet. Detta görs mer bekvämt från SQL Server 2014 och framåt tack vare introduktionen av INDEX
satsen i CREATE TABLE
påstående.
En explicit UPDATE STATISTICS
efter att data har laddats in i den temporära tabellen, och OPTION (RECOMPILE)
tips om påståenden som refererar till tabellen kan behövas för att producera alla de förväntade fördelarna med cachade temporära tabeller inom en modul.
Det är viktigt att endast använda tillfälliga objekt när de ger en tydlig nytta, oftast när det gäller plankvalitet. Överdriven, ineffektiv eller onödig användning av tillfälliga objekt kan leda till tempdb påstående, även när tillfällig objektcachning uppnås.
Optimal temporär objektcachelagring kanske inte räcker för att minska tempdb påstående till acceptabla nivåer i alla fall, även när tillfälliga föremål endast används när det är fullt motiverat. Att använda in-memory-tabellvariabler eller icke-varaktiga in-memory-tabeller kan ge riktade lösningar i sådana fall, även om det alltid finns avvägningar att göra, och ingen enskild lösning representerar för närvarande det bästa alternativet i alla fall.