1

列にクラスター化インデックスがある次のテーブルがあるとします (たとえば、a)

CREATE TABLE Tmp
(
    a int,
    constraint pk_a primary key clustered (a)
)

次に、テーブルに挿入する非常に多数の行のセットが 2 つあるとします。

  • 1 番目のセット) 値は順次増加します (つまり、{0,1,2,3,4,5,6,7,8,9,..., 999999997, 999999998, 99999999})
  • 2 番目のセット) 値は順次減少します (つまり、{99999999,999999998,999999997, ..., 3,2,1,0}

最初のセットと 2 番目のセットに値を挿入すると、パフォーマンスに違いがあると思いますか? もしそうなら、なぜですか?

ありがとう

4

3 に答える 3

5

SQL Serverは通常、挿入する前に、大きな挿入をクラスター化インデックスの順序に並べ替えようとします。

ただし、挿入のソースがテーブル変数である場合、テーブル変数が入力された後にステートメントが再コンパイルされない限り、カーディナリティは考慮されません。これがないと、挿入は1行のみであると想定されます。

以下のスクリプトは、3つの可能なシナリオを示しています。

  1. 挿入ソースはすでに正確に正しい順序になっています。
  2. 挿入ソースは正確に逆の順序になっています。
  3. 挿入ソースは正確に逆の順序ですが、OPTION (RECOMPILE)SQLServerが1,000,000行の挿入に適したプランをコンパイルするために使用されます。

実行計画

予定

3つ目は、挿入された値を最初にクラスター化されたインデックス順に取得するためのソート演算子を備えています。

/*Create three separate identical tables*/
CREATE TABLE Tmp1(a int primary key clustered (a))
CREATE TABLE Tmp2(a int primary key clustered (a))
CREATE TABLE Tmp3(a int primary key clustered (a))

DBCC FREEPROCCACHE;

GO

DECLARE @Source TABLE (N INT PRIMARY KEY (N ASC))

INSERT INTO @Source
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) 
FROM sys.all_columns c1, sys.all_columns c2, sys.all_columns c3

SET STATISTICS TIME ON;

PRINT 'Tmp1'
INSERT INTO Tmp1
SELECT TOP (1000000) N
FROM @Source
ORDER BY N

PRINT 'Tmp2'
INSERT INTO Tmp2
SELECT  TOP (1000000) 1000000 - N
FROM @Source
ORDER BY N

PRINT 'Tmp3'
INSERT INTO Tmp3
SELECT 1000000 - N
FROM @Source
ORDER BY N
OPTION (RECOMPILE)

SET STATISTICS TIME OFF;

結果を確認してクリーンアップします

SELECT object_name(object_id) AS name, 
       page_count, 
       avg_fragmentation_in_percent, 
       fragment_count, 
       avg_fragment_size_in_pages
FROM 
sys.dm_db_index_physical_stats(db_id(), object_id('Tmp1'), 1, NULL, 'DETAILED') 
WHERE  index_level = 0 
UNION ALL 
SELECT object_name(object_id) AS name, 
       page_count, 
       avg_fragmentation_in_percent, 
       fragment_count, 
       avg_fragment_size_in_pages
FROM 
sys.dm_db_index_physical_stats(db_id(), object_id('Tmp2'), 1, NULL, 'DETAILED') 
WHERE  index_level = 0 
UNION ALL 
SELECT object_name(object_id) AS name, 
       page_count, 
       avg_fragmentation_in_percent, 
       fragment_count, 
       avg_fragment_size_in_pages
FROM 
sys.dm_db_index_physical_stats(db_id(), object_id('Tmp3'), 1, NULL, 'DETAILED') 
WHERE  index_level = 0 

DROP TABLE Tmp1, Tmp2, Tmp3

STATISTICS TIME ON結果

+------+----------+--------------+
|      | CPU Time | Elapsed Time |
+------+----------+--------------+
| Tmp1 | 6718 ms  | 6775 ms      |
| Tmp2 | 7469 ms  | 7240 ms      |
| Tmp3 | 7813 ms  | 9318 ms      |
+------+----------+--------------+

フラグメンテーションの結果

+------+------------+------------------------------+----------------+----------------------------+
| name | page_count | avg_fragmentation_in_percent | fragment_count | avg_fragment_size_in_pages |
+------+------------+------------------------------+----------------+----------------------------+
| Tmp1 |       3345 | 0.448430493                  |             17 | 196.7647059                |
| Tmp2 |       3345 | 99.97010463                  |           3345 | 1                          |
| Tmp3 |       3345 | 0.418535127                  |             16 | 209.0625                   |
+------+------------+------------------------------+----------------+----------------------------+

結論

この場合、3つすべてがまったく同じページ数を使用することになりました。ただしTmp2、他の2つはわずか0.4%であるのに対し、99.97%は断片化されています。最初に追加のソートステップが必要だったため、挿入にTmp3最も時間がかかりましたが、最小の断片化のテーブルに対する将来のスキャンの利点に対して、この1回限りのコストを設定する必要があります。

非常に断片化されている理由Tmp2は、以下のクエリから確認できます。

WITH T AS
(
SELECT TOP 3000 file_id, page_id, a
FROM Tmp2
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
ORDER BY a
)
SELECT file_id, page_id, MIN(a), MAX(a)
FROM T 
group by file_id, page_id
ORDER BY MIN(a)

論理的な断片化がゼロの場合、キー値が次に高いページはファイル内で次に高いページになりますが、ページは想定とは正反対の順序になります。

+---------+---------+--------+--------+
| file_id | page_id | Min(a) | Max(a) |
+---------+---------+--------+--------+
|       1 |   26827 |      0 |    143 |
|       1 |   26826 |    144 |    442 |
|       1 |   26825 |    443 |    741 |
|       1 |   26824 |    742 |   1040 |
|       1 |   26823 |   1041 |   1339 |
|       1 |   26822 |   1340 |   1638 |
|       1 |   26821 |   1639 |   1937 |
|       1 |   26820 |   1938 |   2236 |
|       1 |   26819 |   2237 |   2535 |
|       1 |   26818 |   2536 |   2834 |
|       1 |   26817 |   2835 |   2999 |
+---------+---------+--------+--------+

行は降順で到着したため、たとえば、値2834〜2536がページ26818に配置され、新しいページが2535に割り当てられましたが、これはページ26817ではなくページ26819でした。

Tmp2挿入に時間がかかった理由の1つTmp1は、行がページにまったく逆の順序で挿入されるため、挿入するたびに、ページTmp2のスロット配列を書き換えて、前のすべてのエントリを上に移動して、新参者。

于 2012-08-26T21:43:22.543 に答える
0

これは、クラスター化されたインデックスの場合と同様に、ページを順番に割り当てることと関係があります。最初のものでは、それらは自然に一緒にクラスター化します。しかし、2つ目は、ページの場所を順番に昇順に移動し続ける必要があると思います。ただし、実際にはSQLサーバーは概念レベルでしか理解できないため、テストする必要があります。

于 2012-08-26T20:13:54.590 に答える
0

この質問に答えるには、クラスタリングがデータに与える影響と、データが論理的に順序付けられている方法を調べるだけで済みます。昇順でクラスタリングすることにより、より大きな数値がテーブルの最後に追加されます。挿入は非常に高速になります。逆に挿入すると、他の 2 つのレコードの間に挿入されます (ページ分割で読み取られます)。これにより、挿入が遅くなります。これには実際には他の悪影響もあります(フィルファクターを読んでください)。

于 2012-08-26T19:24:36.880 に答える