Denaliシーケンスは、ID列よりも優れたパフォーマンスをすることになっているのはなぜですか?
-
16-10-2019 - |
質問
彼の答えで どちらが良いですか:ID列または生成された一意のID値は? 氏は言う:
SQL Denaliが出てくると、アイデンティティよりも効率的なシーケンスがサポートされますが、自分でより効率的なものを作成することはできません。
私はちょっと確信が持てません。オラクルを知っている シーケンス, 、挿入のトリガーを作成するか、各挿入をストアドプロシージャの呼び出しにカプセル化するか、アドホックインサートを実行するときにシーケンスを適切に使用することを忘れないように祈る必要があります。
シーケンスの利点が非常に明白であるとは思わない。
解決
ここでも答えます。それは方法の内部に関係しています IDENTITY
と SEQUENCE
仕事。
と IDENTITY
, 、SQL Serverは、値をメモリに事前にキャッシュして、すぐに利用できるようにします。見る マーティン・スミスの答え 詳細については。値を使用すると、バックグラウンドプロセスはより多くの値を生成します。想像できるように、このプールはかなり迅速に使い果たす可能性があり、値を生成しているバックグラウンドプロセスに翻弄されます。
と SEQUENCE
, 、SQL Serverを使用すると、キャッシュの大きさを定義できます。 SQL Serverは実際にはキャッシュに値を保持していませんが、現在の値とトップエンド値を保持するだけです。これにより、値を作成するために必要なIOの量が大幅に削減されます。
キャッシュを高く設定しないでください。これにより、使用できる数値の数が減ります。SQLサーバーがクラッシュする場合、使用されていない現在のキャッシュ範囲で指定された値は失われます。
行の挿入については、次のように、列のデフォルト値を指定するだけです。
DEFAULT (NEXT VALUE FOR Audit.EventCounter),
他のヒント
以来 Itzik Ben Ganの記事 10のハードコーディングされたキャッシュサイズが書かれています IDENTITY
変更されたようです。から この接続アイテムに関するコメント
事前配分のサイズは、IDプロパティが定義されている列のデータ型のサイズに基づいています。 SQL Server Integer列の場合、サーバーは1000値の範囲でアイデンティティを事前に割り当てます。 BIGINTデータ型の場合、サーバーは10000値の範囲で事前に割り当てられます。
T-SQLクエリ 本には次の表が含まれていますが、これらの値は文書化されておらず、変更されていないことを強調しています。
+-----------------+-----------+
| DataType | CacheSize |
+-----------------+-----------+
| TinyInt | 10 |
| SmallInt | 100 |
| Int | 1,000 |
| BigInt, Numeric | 10,000 |
+-----------------+-----------+
ここの記事 さまざまなシーケンスキャッシュサイズをテストし、バッチサイズを挿入し、次の結果を思いつきます。
大きな挿入物についてはそれを示しているようです IDENTITY
アウトパフォーマンス SEQUENCE
. 。キャッシュサイズは1,000ではなく、これらの結果は1つのテストにすぎません。さまざまなバッチサイズのインサートを備えたキャッシュサイズ1,000を特に見ると、次の結果が得られました(各バッチサイズを50回試し、結果をμsで常に下回ると集約します。)
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
| | Sequence | Identity |
| Batch Size | Min | Max | Avg | Min | Max | Avg |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
| 10 | 2,994 | 7,004 | 4,002 | 3,001 | 7,005 | 4,022 |
| 100 | 3,997 | 5,005 | 4,218 | 4,001 | 5,010 | 4,238 |
| 1,000 | 6,001 | 19,013 | 7,221 | 5,982 | 8,006 | 6,709 |
| 10,000 | 26,999 | 33,022 | 28,645 | 24,015 | 34,022 | 26,114 |
| 100,000 | 189,126 | 293,340 | 205,968 | 165,109 | 234,156 | 173,391 |
| 1,000,000 | 2,208,952 | 2,344,689 | 2,269,297 | 2,058,377 | 2,191,465 | 2,098,552 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
大きなバッチサイズの場合 IDENTITY
バージョンは一般的に見えます もっと早く.
TSQLクエリの本もその理由を説明しています IDENTITY
シーケンスよりもパフォーマンスの利点を持つことができます。
IDENTITY
テーブル固有です SEQUENCE
そうではありません。ログバッファがフラッシュする前にミッドインサートを攻撃した場合、回復したアイデンティティが挿入物も元に戻すため、回復したアイデンティティが以前のアイデンティティであるかどうかは関係ありません。そのため関連するディスク書き込みをキャッシュします。ただし、シーケンスの場合 は 値として強制され、データベースの外部を含む、あらゆる目的で使用される場合があります。したがって、100万のインサートと1,000のキャッシュサイズを備えた上記の例では、これはさらに千のログフラッシュです。
再現するスクリプト
DECLARE @Results TABLE(
BatchCounter INT,
NumRows INT,
SequenceTime BIGINT,
IdTime BIGINT);
DECLARE @NumRows INT = 10,
@BatchCounter INT;
WHILE @NumRows <= 1000000
BEGIN
SET @BatchCounter = 0;
WHILE @BatchCounter <= 50
BEGIN
--Do inserts using Sequence
DECLARE @SequenceTimeStart DATETIME2(7) = SYSUTCDATETIME();
INSERT INTO dbo.t1_Seq1_cache_1000
(c1)
SELECT N
FROM [dbo].[TallyTable] (@NumRows)
OPTION (RECOMPILE);
DECLARE @SequenceTimeEnd DATETIME2(7) = SYSUTCDATETIME();
--Do inserts using IDENTITY
DECLARE @IdTimeStart DATETIME2(7) = SYSUTCDATETIME();
INSERT INTO dbo.t1_identity
(c1)
SELECT N
FROM [dbo].[TallyTable] (@NumRows)
OPTION (RECOMPILE);
DECLARE @IdTimeEnd DATETIME2(7) = SYSUTCDATETIME();
INSERT INTO @Results
SELECT @BatchCounter,
@NumRows,
DATEDIFF(MICROSECOND, @SequenceTimeStart, @SequenceTimeEnd) AS SequenceTime,
DATEDIFF(MICROSECOND, @IdTimeStart, @IdTimeEnd) AS IdTime;
TRUNCATE TABLE dbo.t1_identity;
TRUNCATE TABLE dbo.t1_Seq1_cache_1000;
SET @BatchCounter +=1;
END
SET @NumRows *= 10;
END
SELECT NumRows,
MIN(SequenceTime) AS MinSequenceTime,
MAX(SequenceTime) AS MaxSequenceTime,
AVG(SequenceTime) AS AvgSequenceTime,
MIN(IdTime) AS MinIdentityTime,
MAX(IdTime) AS MaxIdentityTime,
AVG(IdTime) AS AvgIdentityTime
FROM @Results
GROUP BY NumRows;