Zdá se, že se jedná o oblast s několika mýty a protichůdnými názory.
Jaký je tedy rozdíl mezi proměnnou tabulky a místní dočasnou tabulkou na serveru SQL?
Obsah
Upozornění
Tato odpověď pojednává o „klasických“ tabulkových proměnných zavedených v SQL Server 2000. SQL Server 2014 v paměti OLTP zavádí typy tabulek optimalizovaných pro paměť. Instance proměnných tabulky se v mnoha ohledech liší od diskutovaných) níže ( více podrobností ).
Umístění úložiště
Žádný rozdíl. Oba jsou uloženy v tempdb
.
Viděl jsem, že to naznačuje, že u proměnných tabulky tomu tak není vždy, ale lze to ověřit níže
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Příklad výsledků (zobrazující umístění v tempdb
2 řádky jsou uloženy)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Logické umístění
@table_variables
Se chovají více, jako by byly součástí aktuální databáze, než tabulky #temp
. Pro sloupcové sloupcové proměnné (od roku 2005) bude sloupcové řazení, pokud není výslovně uvedeno, stejné jako u aktuální databáze, zatímco pro tabulky #temp
Použije výchozí řazení tempdb
( Další podrobnosti ). Uživatelsky definované typy dat a kolekce XML musí být v tempdb, aby mohly být použity pro tabulky #temp
, Ale proměnné tabulky je mohou použít z aktuální databáze ( Source ).
SQL Server 2012 představuje obsažené databáze. chování dočasných tabulek v těchto se liší (h/t Aaron)
V uzavřené databázi jsou data dočasných tabulek seřazena do řazení uzavřené databáze.
- Všechna metadata spojená s dočasnými tabulkami (například názvy tabulek a sloupců, indexy atd.) Budou ve třídění katalogů.
- Pojmenovaná omezení nelze použít v dočasných tabulkách.
- Dočasné tabulky se nemusí vztahovat na uživatelem definované typy, kolekce schémat XML ani na uživatelem definované funkce.
Viditelnost do různých rozsahů
@table_variables
Lze získat pouze v rámci dávky a rozsahu, ve kterém jsou deklarovány. #temp_tables
Jsou přístupné v rámci podřízených šarží (vnořené spouště, procedura, volání exec
). #temp_tables
Vytvořený ve vnějším oboru (@@NESTLEVEL=0
) Může také překrývat šarže, protože přetrvávají až do ukončení relace. Žádný typ objektu nelze vytvořit v podřízené dávce a přistupovat k němu ve volajícím rozsahu, jak je popsáno dále (globální ##temp
Tabulky mohou být).
Životnost
@table_variables
Jsou vytvářeny implicitně, když je spuštěna dávka obsahující příkaz DECLARE @.. TABLE
(Před spuštěním jakéhokoli uživatelského kódu v této dávce) a na konci jsou implicitně zrušena.
Ačkoli vám analyzátor neumožňuje vyzkoušet a použít proměnnou tabulky před příkazem DECLARE
, implicitní vytvoření je vidět níže.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
Jsou vytvářeny explicitně, když je zaznamenán TSQL CREATE TABLE
Příkaz a lze je explicitně zrušit pomocí DROP TABLE
Nebo budou implicitně zrušeny po ukončení dávky (pokud byly vytvořeny v podřízené dávce s @@NESTLEVEL > 0
) Nebo když relace skončí jinak.
Pozn .: V rámci uložených rutin oba typy objektů lze uložit do mezipaměti , spíše než opakovaně vytvářet a rušit nové tabulky. Existují určitá omezení, kdy může dojít k tomuto ukládání do mezipaměti, ale je možné je porušit pro #temp_tables
, Ale kterým omezením @table_variables
Přesto zabránit. Režijní náklady pro tabulky #temp
V mezipaměti jsou mírně větší než pro proměnné tabulky jak je zde znázorněno .
Metadata objektu
To je v podstatě stejné pro oba typy objektů. Je uložen v základních tabulkách systému v tempdb
. Je jednodušší vidět tabulku #temp
, Protože OBJECT_ID('tempdb..#T')
může být použito pro vložení do systémových tabulek a interně generovaný název je těsněji korelován s názvem definovaným v [$ CREATE TABLE
= CODE!] Prohlášení. Pro tabulkové proměnné funkce object_id
Nefunguje a interní název je generován výhradně systémem bez vztahu k názvu proměnné. Níže ukazuje, že metadata jsou stále přítomna zadáním názvu (doufejme, jedinečného) sloupce. U tabulek bez jedinečných názvů sloupců lze object_id určit pomocí DBCC PAGE
, pokud nejsou prázdné.
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
Výstup
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
Transakce
Operace na @table_variables
Jsou prováděny jako systémové transakce, nezávislé na jakékoli transakci externího uživatele, zatímco ekvivalentní operace #temp
Tabulky by byly prováděny jako součást samotné uživatelské transakce. Z tohoto důvodu bude příkaz ROLLBACK
ovlivňovat tabulku #temp
, Ale @table_variable
Zůstane nedotčen.
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
Protokolování
Oba generují záznamy protokolu do protokolu transakcí tempdb
. Běžná mylná představa je, že tomu tak není v případě proměnných tabulky, takže skript prokazující, že je níže, deklaruje proměnnou tabulky, přidá několik řádků, poté je aktualizuje a odstraní.
Protože proměnná tabulky je vytvořena a zrušena implicitně na začátku a na konci dávky, je nutné použít více šarží, aby bylo možné zobrazit úplné protokolování.
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
Vrací se
Pokud jsem byl schopen rozeznat operace na obou generují zhruba stejné množství protokolování.
Zatímco množství protokolování je velmi podobné, jeden důležitý rozdíl spočívá v tom, že záznamy protokolu související s tabulkami #temp
Nemohou být vymazány, dokud žádná transakce obsahující uživatele nedokončí tak dlouho trvající transakci, která v určitém okamžiku zápisy do tabulek #temp
zabrání zkrácení protokolu v tempdb
, zatímco autonomní transakce vytvořené pro proměnné tabulky nikoli.
Proměnné tabulky nepodporují TRUNCATE
, takže mohou být v nevýhodě protokolování, když je vyžadováno odebrání všech řádků z tabulky (i když u velmi malých tabulek DELETE
přesto to může fungovat lépe) )
kardinálnost
Mnoho plánů provádění zahrnujících proměnné tabulky zobrazí jediný řádek odhadovaný jako výstup z nich. Kontrola vlastností proměnné tabulky ukazuje, že SQL Server věří, že proměnná tabulky má nulové řádky (Proč je odhadováno, že 1 řádek bude emitován z tabulky nulových řádků) od @Paul White zde ).
Výsledky zobrazené v předchozí části však ukazují přesný počet rows
v sys.partitions
. Problém je, že ve většině případů jsou příkazy odkazující na proměnné tabulky kompilovány, zatímco je tabulka prázdná. Pokud je příkaz (znovu) zkompilován po naplnění @table_variable
, Bude místo toho použito pro kardinálnost tabulky (k tomu může dojít v důsledku explicitního recompile
nebo snad proto, že příkaz také odkazuje na jiný objekt což způsobí odložené kompilace nebo kompilace.)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
Plán ukazuje přesný odhadovaný počet řádků po odloženém kompilaci.
V SQL Server 2012 SP2 je zaveden příznak trasování 2453. Více podrobností najdete v části „Relational Engine“ zde .
Je-li tento příznak trasování povolen, může to způsobit, že automatické rekompilace vezmou v úvahu změnou mohutnost, jak bude velmi brzy probráno.
Pozn .: Na Azure v úrovni kompatibility 150 je kompilace příkazu nyní odložena do prvního spuštění . To znamená, že již nebude předmětem problému s odhadem nulové řady.
Žádné statistiky sloupců
Přesnější kardinálnost tabulky však neznamená, že odhadovaný počet řádků bude přesnější (pokud však neprovedete operaci na všech řádcích v tabulce). SQL Server vůbec neudržuje statistiku sloupců pro proměnné tabulky, takže se vrátí zpět k odhadům založeným na predikátu porovnání (např. 10% tabulky bude vráceno za =
Proti neobvyklému sloupci nebo 30% pro srovnání >
). V tabulkách kontrastů jsou udržovány tabulky #temp
.
SQL Server udržuje počet změn provedených v každém sloupci. Pokud počet úprav od kompilace plánu překročí prahovou hodnotu rekompilace (RT), bude plán překompilován a statistiky aktualizovány. Hodnota RT závisí na typu a velikosti tabulky).
Od Plánování mezipaměti v SQL Server 2008
RT se vypočte následujícím způsobem. (n odkazuje na mohutnost tabulky při kompilaci plánu dotazů.)
Stálá tabulka
- Pokud n <= 500, RT = 500.
- Pokud n> 500, RT = 500 + 0,20 * n.Dočasná tabulka
- Pokud n <6, RT = 6.
- Pokud 6 <= n <= 500, RT = 500.
- Pokud n> 500, RT = 500 + 0,20 * n.
Tabulka proměnná
- RT neexistuje). K opakování proto nedochází kvůli změnám v kardinalitě proměnných tabulky. (Ale viz poznámka o TF 2453 níže)
nápovědu KEEP PLAN
lze použít k nastavení tabulek RT pro #temp
tabulky stejné jako pro trvalé tabulky.
Čistý efekt toho všeho je, že často jsou plány provádění generované pro tabulky #temp
Řádově lepší než pro @table_variables
, Když je zapojeno mnoho řádků, protože SQL Server má lepší informace pro práci.
NB1: Proměnné tabulky nemají statistiku, ale stále mohou nastat pod názvem trasovacího příznaku 2453 událost „Statistics Changed“ (neplatí pro „triviální“ plány). Zdá se, že k tomu dochází pod stejnými prahovými hodnotami rekompilace, jaké jsou uvedeny u dočasných tabulek výše s další, pokud N=0 -> RT = 1
. tj. všechny příkazy zkompilované, když je proměnná tabulky prázdná, skončí získáním kompilace a opravou TableCardinality
při prvním spuštění, když nejsou prázdné. Kardinalita kompilační časové tabulky je uložena v plánu a pokud je příkaz spuštěn znovu se stejnou kardinálností (buď kvůli toku kontrolních příkazů nebo opětovnému použití plánu uloženého v mezipaměti), nenastane žádná kompilace.
Poznámka 2: U dočasných tabulek uložených v mezipaměti uložených procedur je příběh rekompilace mnohem komplikovanější, než je popsáno výše. Viz Dočasné tabulky v uložených procedurách pro všechny krvavé detaily.
Rekompilace
Stejně jako výše popsané rekompily založené na modifikacích popsané výše #temp
Tabulky mohou být také spojeny s další kompilace jednoduše proto, že umožňují operace, které jsou zakázány pro proměnné tabulky, které spouštějí kompilaci (např. DDL změny CREATE INDEX
, ALTER TABLE
)
Zamykání
bylo uvedeno , že proměnné tabulky se neúčastní zamykání. Toto není ten případ. Spuštění níže uvedených výstupů na kartě SSMS zprávy obsahuje podrobnosti o uzamčených a uvolněných zámcích pro příkaz insert.
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
Pro dotazy, které SELECT
z proměnných tabulky, Paul White v komentářích upozorňuje, že tyto automaticky přicházejí s implicitní NOLOCK
nápovědou. To je znázorněno níže
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
Dopad toho na uzamykání však může být docela malý.
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
Žádný z těchto návratů nemá za následek pořadí klíčů indexu, což znamená, že SQL Server použil alokace objednané kontroly pro oba.
Spustil jsem výše uvedený skript dvakrát a výsledky pro druhé spuštění jsou níže
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
Výstup zamykání pro proměnnou tabulky je skutečně extrémně minimální, protože SQL Server pouze získává zámek stability schématu na objektu. Ale pro tabulku #temp
Je téměř stejně lehké, že odstraňuje zámek na úrovni objektu S
. Při práci s tabulkami READ UNCOMMITTED
Lze samozřejmě explicitně určit úroveň izolace NOLOCK
nebo #temp
.
Podobně jako při protokolování může okolní uživatelská transakce znamenat, že zámky jsou drženy déle pro tabulky #temp
. Se skriptem níže
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
při spuštění mimo výslovnou transakci uživatele v obou případech je jediným zámkem vráceným při kontrole sys.dm_tran_locks
sdílený zámek na DATABASE
.
Při odkomentování BEGIN TRAN ... ROLLBACK
Je vráceno 26 řádků, které ukazují, že zámky jsou drženy jak na samotném objektu, tak na řádcích systémových tabulek, aby bylo možné vrátit zpět a zabránit jiným transakcím ve čtení nepřijatých dat. Ekvivalentní operace s proměnnou tabulky nepodléhá návratu s transakcí uživatele a není třeba tyto zámky držet, abychom je mohli zkontrolovat v dalším příkazu, ale trasování zámků získaných a uvolněných v Profiler nebo pomocí příznaku trasování 1200 ukazuje, že stále existuje mnoho zámkových událostí nastat.
Indexy
U verzí před SQL Server 2014 mohou být indexy vytvořeny pouze implicitně na proměnných tabulky jako vedlejší účinek přidání jedinečného omezení nebo primárního klíče. To samozřejmě znamená, že jsou podporovány pouze jedinečné indexy. Neunikající neklastrovaný index v tabulce s jedinečným klastrovaným indexem lze simulovat jednoduchým prohlášením UNIQUE NONCLUSTERED
A přidáním klíče CI na konec požadovaného klíče NCI (SQL Server by do to i přesto za scénami , i když lze specifikovat nejedinečnou NCI)
Jak bylo ukázáno dříve, různé index_option
S mohou být specifikovány v deklaraci omezení včetně DATA_COMPRESSION
, IGNORE_DUP_KEY
A FILLFACTOR
(ačkoli nemá smysl nastavovat jeden protože by to jen změnilo opětovné vytvoření indexu a nemůžete znovu vytvořit indexy u proměnných tabulky!)
Proměnné tabulky navíc nepodporují sloupce INCLUDE
d, filtrované indexy (do roku 2016) ani rozdělení oddílů, tabulky #temp
Ano (schéma oddílů musí být vytvořeno v tempdb
).
Indexy v SQL Server 2014
Ne jedinečné indexy lze deklarovat vložené v definici proměnné tabulky v SQL Server 2014. Příklad syntaxe je níže.
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
Indexy v SQL Server 2016
Z CTP 3.1 je nyní možné deklarovat filtrované indexy pro proměnné tabulky. Do RTM it může v případě, že zahrnuté sloupce jsou také povoleny, i když jsou pravděpodobně to nezmění na SQL16 kvůli omezením zdrojů =
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
Parallelism
Dotazy, které vkládají do (nebo jinak upravují) @table_variables
, Nemohou mít paralelní plán, #temp_tables
Nejsou tímto způsobem omezeny.
V tomto přepisu je zřejmé řešení, které umožňuje, aby část SELECT
probíhala paralelně, ale to skončilo použitím skryté dočasné tabulky (za scénami)
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
V dotazech neexistuje takové omezení, které vybere z proměnných tabulky jak je znázorněno v mé odpovědi zde
Ostatní funkční rozdíly
#temp_tables
Nelze použít uvnitř funkce. @table_variables
Lze použít uvnitř UDF skalárních nebo multi-příkazových tabulek.@table_variables
Nemůže mít pojmenovaná omezení.@table_variables
Nemůže být SELECT
- ed INTO
, ALTER
- ed, TRUNCATE
d nebo být cílem DBCC
příkazy jako DBCC CHECKIDENT
nebo SET IDENTITY INSERT
a nepodporují rady v tabulce, jako je například WITH (FORCESCAN)
CHECK
omezení na proměnné tabulky nejsou optimalizátorem zvažována pro zjednodušení, implikované predikáty nebo detekci rozporů.PAGELATCH_EX
. ( Příklad )Pouze paměť?
Jak bylo uvedeno na začátku, oba se uloží na stránky v tempdb
. Neřešil jsem však, zda došlo k nějakému rozdílu v chování, pokud jde o zápis těchto stránek na disk.
Teď jsem na tom provedl malé množství testů a zatím jsem žádný takový rozdíl neviděl. Ve specifickém testu, který jsem provedl na své instanci stránek SQL Server 250, se zdá být mezní bod, než se datový soubor zapíše.
Poznámka: K níže uvedenému chování již nedochází v SQL Server 2014 nebo SQL Server 2012 SP1/CU10 nebo SP2/CU1 dychtivý zapisovatel již není tak dychtivý zapisovat stránky na disk. Další podrobnosti o této změně najdete na SQL Server 2014: tempdb Hidden Performance Gem .
Spuštění skriptu níže
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
A monitorování zapisuje do datového souboru tempdb
s Process Monitor, který jsem žádný neviděl (s výjimkou příležitostných na spouštěcí stránku databáze na offset 73 728). Po změně 250
Na 251
Jsem začal vidět spisy, jak je uvedeno níže.
Snímek nahoře ukazuje 5 * 32 zápisů stránek a jeden zápis jedné stránky, což znamená, že 161 stránek bylo zapsáno na disk. Při testování pomocí proměnných tabulky jsem dostal stejný mezní bod 250 stránek. Níže uvedený skript ukazuje jiný způsob pohledu na sys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
Ukazuje, že na disk bylo zapsáno 192 stránek a špinavá vlajka byla vymazána. Ukazuje také, že zápis na disk neznamená, že stránky budou okamžitě vyklizeny z fondu vyrovnávacích pamětí. Dotazy proti této proměnné tabulky lze stále plně uspokojit z paměti.
Na nečinném serveru s max server memory
Nastaveným na 2000 MB
A DBCC MEMORYSTATUS
Vykazující stránky s vyrovnávací pamětí přidělené jako přibližně 1 843 000 KB (cca 23 000 stránek) jsem vložil do výše uvedených tabulek v dávkách 1 000 řádků/stránek a pro každou zaznamenanou iteraci.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
Proměnná tabulka i tabulka #temp
Daly téměř identické grafy a dokázaly do značné míry vytěsnit fond vyrovnávacích pamětí, než se dostaly do bodu, kdy nebyly zcela uchovány v paměti, takže se nezdá, že by existovaly nějaké zvláštní omezení toho, kolik paměti může spotřebovat.
Je několik věcí, na které bych rád upozornil spíše na konkrétní zkušenosti než na studium. Jako DBA jsem velmi nová, takže mě prosím v případě potřeby opravte.