ゲスト作成者:Derik Hammer(@SQLHammer)
最近、Aaron Bertrandが、有害で広範囲にわたるSQLServerのパフォーマンスの神話についてブログに書いています。このブログシリーズの延長として、私はこの一般的な神話を反証します:
マニュアルを読む
ソースに直接アクセスして、テーブル変数を含むテーブルに関するBooksOnlineの記事を調べました。この記事ではテーブル変数を使用する利点について言及していますが、テーブル変数が100%メモリ内にあるという事実は著しく欠落しています。
ただし、肯定が欠落していることは否定を意味するものではありません。インメモリOLTPテーブルがリリースされてから、インメモリ処理に関するBOLのドキュメントがさらに増えました。そこで、メモリ最適化を使用して一時テーブルとテーブル変数を高速化する方法に関するこの記事を見つけました。
記事全体は、一時オブジェクトにメモリ内のOLTP機能を使用させる方法を中心に展開しており、ここで私が探していた肯定的なものを見つけました。
「従来のテーブル変数は、tempdbデータベース内のテーブルを表します。パフォーマンスを大幅に向上させるために、テーブル変数をメモリ最適化できます。」テーブル変数はメモリ内の構成ではありません。インメモリテクノロジを使用するには、メモリに最適化されたTYPEを明示的に定義し、そのTYPEを使用してテーブル変数を定義する必要があります。
証明する
ドキュメンテーションは1つのことですが、私自身の目でそれを見るのはまったく別のことです。一時テーブルがtempdbにオブジェクトを作成し、データをディスクに書き込むことを知っています。最初に、一時テーブルでどのように見えるかを示し、次に同じ方法を使用して、テーブル変数が同じように機能するという仮説を検証します。
ログレコード分析
このクエリは、チェックポイントを実行してクリーンな開始点を提供し、ログレコードの数とログに存在するトランザクション名を表示します。
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;
T-SQLを繰り返し実行すると、一貫した3つのレコード数が得られましたSQL Server2016SP1の場合。
これにより、一時テーブルが作成され、オブジェクトレコードが表示され、これがtempdb内の実際のオブジェクトであることを証明します。
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;
ここで、ログレコードを再度表示します。 CHECKPOINTコマンドを再実行しません。
21個のログレコードが書き込まれ、これらがディスク上の書き込みであることを証明し、CREATETABLEがこれらのログレコードに明確に含まれています。
これらの結果をテーブル変数と比較するために、CHECKPOINTを実行してから、以下のT-SQLを実行して、テーブル変数を作成することにより、実験をリセットします。
USE tempdb; GO DECLARE @var TABLE (id int NULL); SELECT name FROM sys.objects o WHERE is_ms_shipped = 0;
ここでも、新しいオブジェクトレコードがあります。ただし、今回は一時テーブルよりも名前がランダムになっています。
82個の新しいログレコードとトランザクション名があり、私の変数がログに書き込まれていること、つまりディスクに書き込まれていることを証明しています。
実際にはメモリ内
今度は、ログレコードを非表示にするときです。
インメモリOLTPファイルグループを作成してから、メモリ最適化テーブルタイプを作成しました。
USE Test; GO CREATE TYPE dbo.inMemoryTableType AS TABLE ( id INT NULL INDEX ix1 ) WITH (MEMORY_OPTIMIZED = ON); GO
もう一度チェックポイントを実行してから、メモリ最適化テーブルを作成しました。
USE Test; GO DECLARE @var dbo.inMemoryTableType; INSERT INTO @var (id) VALUES (1) SELECT * from @var; GO
ログを確認した後、ログアクティビティは表示されませんでした。この方法は実際には100%メモリ内にあります。
お持ち帰り
テーブル変数は、一時テーブルがtempdbを使用する方法と同様にtempdbを使用します。テーブル変数はメモリ内の構成ではありませんが、メモリに最適化されたユーザー定義のテーブルタイプを使用する場合はそれらになる可能性があります。多くの場合、一時テーブルはテーブル変数よりもはるかに優れた選択肢であることがわかります。これの主な理由は、テーブル変数に統計がなく、SQL Serverのバージョンと設定に応じて、行の見積もりが1行または100行になるためです。どちらの場合も、これらは推測であり、クエリ最適化プロセスで有害な誤った情報になります。
これらの機能の違いの一部は時間の経過とともに変化する可能性があることに注意してください。たとえば、SQL Serverの最近のバージョンでは、インラインインデックス構文を使用してテーブル変数に追加のインデックスを作成できます。次の表には3つのインデックスがあります。主キー(デフォルトではクラスター化)、および2つの非クラスター化インデックス:
DECLARE @t TABLE ( a int PRIMARY KEY, b int, INDEX x (b, a DESC), INDEX y (b DESC, a) );
MartinSmithがテーブル変数と#tempテーブルの違いを徹底的に詳しく説明しているDBAStackExchangeには、すばらしい答えがあります。
- SQL Serverの一時テーブルとテーブル変数の違いは何ですか?