Database
 sql >> Datenbank >  >> RDS >> Database

Leistungsmythen:Tabellenvariablen sind immer im Arbeitsspeicher

Gastautor:Derik Hammer (@SQLHammer)


Kürzlich hat Aaron Bertrand über schädliche, allgegenwärtige Mythen zur SQL Server-Leistung gebloggt. Als Erweiterung dieser Blogserie werde ich diesen weit verbreiteten Mythos widerlegen:

"Tabellenvariablen sind immer im Speicher und daher schneller als temporäre Tabellen."

Lesen des Handbuchs

Ich ging direkt zur Quelle und sah mir den Artikel von Books Online über Tabellen an, der Tabellenvariablen enthält. Obwohl der Artikel auf die Vorteile der Verwendung von Tabellenvariablen verweist, fehlt auffallend die Tatsache, dass sie zu 100 % im Arbeitsspeicher sind.

Eine fehlende Bejahung impliziert jedoch keine Verneinung. Seit der Veröffentlichung von In-Memory-OLTP-Tabellen gibt es jetzt viel mehr Dokumentation in BOL für die In-Memory-Verarbeitung. Dort habe ich diesen Artikel gefunden, in dem es darum geht, temporäre Tabellen und Tabellenvariablen durch Speicheroptimierung zu beschleunigen.

Der gesamte Artikel dreht sich darum, wie Sie Ihre temporären Objekte dazu bringen, die In-Memory-OLTP-Funktion zu verwenden, und hier habe ich die Bestätigung gefunden, nach der ich gesucht habe.

"Eine herkömmliche Tabellenvariable stellt eine Tabelle in der tempdb-Datenbank dar. Für eine viel schnellere Leistung können Sie Ihre Tabellenvariable speicheroptimieren."

Tabellenvariablen sind keine In-Memory-Konstrukte. Um die In-Memory-Technologie zu verwenden, müssen Sie explizit einen TYP definieren, der speicheroptimiert ist, und diesen TYP verwenden, um Ihre Tabellenvariable zu definieren.

Beweisen Sie es

Dokumentation ist eine Sache, aber es mit eigenen Augen zu sehen, eine ganz andere. Ich weiß, dass temporäre Tabellen Objekte in tempdb erstellen und Daten auf die Festplatte schreiben. Zuerst werde ich Ihnen zeigen, wie das für die temporären Tabellen aussieht, und dann werde ich dieselbe Methode verwenden, um die Hypothese zu validieren, dass sich Tabellenvariablen auf die gleiche Weise verhalten.

Logaufzeichnungsanalyse

Diese Abfrage führt einen CHECKPOINT aus, um mir einen sauberen Ausgangspunkt zu geben, und zeigt dann die Anzahl der Protokolldatensätze und die Transaktionsnamen an, die im Protokoll vorhanden sind.

USE tempdb;
GO
 
CHECKPOINT;
GO
 
SELECT COUNT(*) [Count] 
  FROM sys.fn_dblog (NULL, NULL);
 
SELECT [Transaction Name]
  FROM sys.fn_dblog (NULL, NULL)
  WHERE [Transaction Name] IS NOT NULL;


Das wiederholte Ausführen von T-SQL führte zu einer konsistenten Anzahl von drei Datensätzen auf SQL Server 2016 SP1.

Dadurch wird eine temporäre Tabelle erstellt und der Objektdatensatz angezeigt, was beweist, dass dies ein echtes Objekt in tempdb ist.

USE tempdb;
GO
 
DROP TABLE IF EXISTS #tmp;
GO
 
CREATE TABLE #tmp (id int NULL);
 
SELECT name
  FROM sys.objects o
  WHERE is_ms_shipped = 0;

Jetzt zeige ich nochmal die Log-Einträge. Ich werde den CHECKPOINT-Befehl nicht erneut ausführen.

Einundzwanzig Protokolleinträge wurden geschrieben, was beweist, dass es sich um Schreibvorgänge auf der Festplatte handelt, und unsere CREATE TABLE ist eindeutig in diesen Protokolleinträgen enthalten.

Um diese Ergebnisse mit Tabellenvariablen zu vergleichen, setze ich das Experiment zurück, indem ich CHECKPOINT ausführe und dann das folgende T-SQL ausführe, wodurch eine Tabellenvariable erstellt wird.

USE tempdb;
GO
 
DECLARE @var TABLE (id int NULL);
 
SELECT name
  FROM sys.objects o
  WHERE is_ms_shipped = 0;

Wieder einmal haben wir einen neuen Objektdatensatz. Diesmal ist der Name jedoch willkürlicher als bei temporären Tabellen.

Es gibt zweiundachtzig neue Protokolldatensätze und Transaktionsnamen, die beweisen, dass meine Variable in das Protokoll und damit auf die Festplatte geschrieben wird.

Eigentlich im Speicher

Jetzt ist es für mich an der Zeit, die Logeinträge verschwinden zu lassen.

Ich habe eine speicherinterne OLTP-Dateigruppe und dann einen speicheroptimierten Tabellentyp erstellt.

USE Test;
GO
 
CREATE TYPE dbo.inMemoryTableType  
  AS TABLE 
  ( id INT NULL INDEX ix1 )
  WITH (MEMORY_OPTIMIZED = ON);  
GO

Ich habe den CHECKPOINT erneut ausgeführt und dann die speicheroptimierte Tabelle erstellt.

USE Test;
GO
 
DECLARE @var dbo.inMemoryTableType;
 
INSERT INTO @var (id) VALUES (1)
 
SELECT * from @var;  
GO

Nach Überprüfung des Protokolls konnte ich keine Protokollaktivität feststellen. Diese Methode ist tatsächlich zu 100 % speicherintern.

Zum Mitnehmen

Tabellenvariablen verwenden tempdb ähnlich wie temporäre Tabellen tempdb verwenden. Tabellenvariablen sind keine speicherinternen Konstrukte, können aber zu solchen werden, wenn Sie speicheroptimierte benutzerdefinierte Tabellentypen verwenden. Oft finde ich temporäre Tabellen eine viel bessere Wahl als Tabellenvariablen. Der Hauptgrund dafür ist, dass Tabellenvariablen keine Statistiken haben und die Zeilenschätzungen je nach SQL Server-Version und -Einstellungen 1 Zeile oder 100 Zeilen betragen. In beiden Fällen sind dies Vermutungen und werden zu schädlichen Fehlinformationen in Ihrem Abfrageoptimierungsprozess.

Beachten Sie, dass sich einige dieser Funktionsunterschiede im Laufe der Zeit ändern können – in neueren Versionen von SQL Server können Sie beispielsweise zusätzliche Indizes für eine Tabellenvariable mithilfe der Inline-Indexsyntax erstellen. Die folgende Tabelle hat drei Indizes; der Primärschlüssel (standardmäßig geclustert) und zwei nicht geclusterte Indizes:

DECLARE @t TABLE
(
  a int PRIMARY KEY,
  b int,
  INDEX x (b, a DESC),
  INDEX y (b DESC, a)
);

Es gibt eine großartige Antwort auf DBA Stack Exchange, in der Martin Smith die Unterschiede zwischen Tabellenvariablen und #temp-Tabellen ausführlich beschreibt:

  • Was ist der Unterschied zwischen einer temporären Tabelle und einer Tabellenvariablen in SQL Server?

Über den Autor

Derik ist ein Datenexperte und frisch gebackener Microsoft Data Platform MVP mit Schwerpunkt auf SQL Server. Seine Leidenschaft konzentriert sich auf Hochverfügbarkeit, Disaster Recovery, kontinuierliche Integration und automatisierte Wartung. Seine Erfahrung umfasst langfristige Datenbankverwaltung, Beratung und unternehmerische Unternehmungen in der Finanz- und Gesundheitsbranche. Derzeit ist er Senior Database Administrator und verantwortlich für das Database Operations Team im Subway Franchise World Headquarters. Wenn er nicht auf der Uhr ist oder auf SQLHammer.com bloggt, widmet Derik seine Zeit der #sqlfamily als Kapitelleiter der FairfieldPASS SQL Server-Benutzergruppe in Stamford, CT.