私は大きなテーブルを持っています。テーブルの行数は30億を超え、このテーブルのデータ領域は約120 GBです。
Intel Xeon CPU E5645 @ 2.4GHz(2プロセッサ)、24 CPU、64Gメモリ、64ビットWindows Server 2008 R2エンタープライズ。
走る
create unique clustered index MyTable_IXC on tblFactFoo(barKey) on [PRIMARY]
しかし、それは6時間以上かかりました(実際には、6時間後に重複したキーのエラーが報告されました)。
それを実行すると、cpuは10%未満であり、ディスクIOは20M/s未満、通常は約15M/sでした。そのような強力なハードウェア。
クエリの実行時に待機タイプを確認する必要があります。おそらく、大きなテーブルにインデックスを作成すると、大量の読み取りと書き込みが発生するため、より高速なディスクが必要になります。
簡単に言えば、120ギガテーブルを読み取り、クラスタリングキー(tempdbに大量の流出を引き起こし、tempdbに100ギガを書き込む可能性があります)に基づいてソートしてから、クラスタ化インデックスをデータベースに書き込む必要があります。 120ギグの書き込みが発生します。
さらに、テーブル上のすべての非クラスター化インデックスを同時に再構築する必要があるため、それらすべても(非クラスター化インデックスの再構築に伴うすべてのメモリスピルとともに)読み取りと再書き込みを行う必要があります。
非クラスター化インデックスを無効にし、クラスター化インデックスが再構築された後に手動で作成することで、処理速度を上げることができます。非クラスター化インデックスが作成されるまでクエリのパフォーマンスが低下するため、非クラスター化インデックスの構築中は、エンドユーザーがシステムに触れないようにする必要があります。
Enterprise Editionを想定すると、一般的に使用されるインデックスを作成してから、ユーザーを元に戻し、他のインデックスをオンラインで作成して、ユーザーが作業できるようにすることができます。
何を見ていても、本当に長いメンテナンスウィンドウで、ディスクをできる限り激しく叩きます。
評価するべきいくつかの事柄はここにあります:
SORT_IN_TEMPDB
オン。これにより、IOパターンが大幅に改善されます(順次IOが増え、最終インデックスの断片化が減ります)。または、最初に正しくソートされたデータをロードします。そうすれば、インデックスを作成する必要はまったくありません。これにはいくつかの欠点がありますが、検討する価値があります。最善の解決策は、一括読み込みにORDER
ヒントを使用してパーティション分割クラスター化インデックスに読み込むことです。パーティションごとに1つの一括インポートストリームと、CPUまたは物理ディスクごとに1つのパーティション。
私が個人的にこれを行う方法:
クラスターインデックスを作成/再作成するたびに、サーバーはページの順序付けを開始します。これは、かなりのリソースを必要とする手順です。あなたのテーブルは大きいです。可能であれば、テーブルをいくつかの小さなテーブルに分割する(つまり、データの正規化を行う)ことをお勧めします。または、このテーブルの空のコピーを作成し、空のテーブルにクラスターインデックスを追加し、メインテーブルからすべてのデータをインポートしてから、メインテーブルを削除します。
私はこのようなものを意味します-
CREATE TABLE dbo.tblFactFoo_New
(
barKey INT NOT NULL
, ...
)
CREATE UNIQUE CLUSTERED INDEX tblFactFoo_IXC ON tblFactFoo_New(barKey) ON [PRIMARY]
INSERT INTO dbo.tblFactFoo_New(barKey, ...)
SELECT barKey, ...
FROM dbo.tblFactFoo
GROUP BY barKey, ... -- without duplicates
ORDER BY barKey
DROP TABLE dbo.tblFactFoo
sp_rename 'dbo.tblFactFoo_New', 'dbo.tblFactFoo'
SQLコマンドの速度を上げるには、データベースを適切に設定する必要があります。したがって、データベースが別のディスクに保存され、masterとtempdbがそれぞれのディスクにあることを願っています。
とは言っても、インデックスの作成に影響を与えるいくつかの要因があります:テーブルが既に並べ替えられている場合、これをHEAPテーブル上に構築しているように見えるので、並べ替えは行われておらず、他のコンポーネントは列のタイプですインデックスを作成しています。クラスター化インデックスに含まれる情報は、列の数または列のバイトサイズ(どちらか早い方)によって制限されるため、一部の列はクラスター化インデックスとしては適していません。
ヒープテーブルに一意のインデックスを作成しているので、重複する値がないように、最初にそれをクリーンアップする必要があります。これにより、インデックスを再構築する必要がなくなります。
したがって、インデックス作成クエリを実行する前に、これを最初に実行します
SELECT
barKey
FROM
(
SELECT
barKey,
COUNT(barKey) AS NoOfDuplicates
FROM
dbo.tblFactFoo WITH(NOLOCK)
GROUP BY
barKey
)
WHERE
NoOfDuplicates > 1;
これを実行して重複レコードを処理した後、以下を実行できます。これは追加のディスク領域を使用するため、少なくともそのテーブルのサイズと同じだけの領域が必要になることに注意してください。
CREATE UNIQUE CLUSTERED INDEX IXC_MyTable ON dbo.tblFactFoo(barKey) WITH(SORT_IN_TEMPDB)
GO
これにより、tempdbデータベースで強制的に並べ替え(インデックスの作成時に必要)が発生し、データが転送されて置き換えられます。
別の方法としては、同じ名前やclumnsなどで重複するテーブルを作成し、そこにクラスター化されたキーを追加してから、レコードを追加してから次のコマンドを実行します。
MERGE INTO dbo.tblFactFoo AS source
USING dbo.tblFactFooIndexed AS destination ON source.barKey = destination.barKey
WHEN NOT MATCHED BY source THEN
INSERT INTO destination(col1, col2, barKey etc) VALUES (source.col1, source.col2, source.barKey etc)
WHEN MATCHED BY source AND (add extra conditions here if needed) THEN
-- INSERT / UPDATE or DELETE depending on how you want to handle duplicate keys
これはセット操作であるため、SQLサーバーは行で操作するよりもセットで操作する方が速いため、理論的にはこれははるかに速く機能するはずです。完了したら、最初のテーブルをドロップし、2番目のテーブルの名前を変更します。
MERGEコマンドについてさらにヘルプが必要な場合は、MSDNのリンクにここにあります。 http://msdn.Microsoft.com/en-us/library/bb510625.aspx