web-dev-qa-db-ja.com

非常に大きな主キーインデックスを再構築する

AzureでホストされているSQLデータベースがあります。問題は、サイズが制御不能になっていることです。主キーのクラスター化インデックスに最大99%の断片化が見られます。

_online=on_オプションを使用して他のすべてのインデックスを再構築できますが、パフォーマンスには影響しません。 PKクラスター化インデックスの1つのサイズが200GBを超えているため、このインデックスではREBUILD...WITH (ONLINE=ON)がロックを引き起こします。

すべてのタイムゾーンのユーザーがサイトにアクセスしているので、オフラインでインデックスを再構築できる時間を見つけることができません。

サイトでダウンタイムを発生させずに大きなインデックスを再構築するための最良の戦略は何ですか?

断片化は99%なので、再編成しても効果がないと思います。問題は、オンラインでもテーブルがロックされることです。主な問題は、インデックスが200GBを超えることです。主キーは整数です。

13
Techy

少し遅いですが、良い質問だと思いますので、この問題に役立つか、少なくともこの問題に関するいくつかの追加のアイデア/解説を刺激することを期待して、回答を提出します。

まず、これを行っているかどうかはわかりませんが、インデックスの断片化レベルが高いと常にパフォーマンスが低下するとは想定しないでください。古くなった統計(例 sys.dm_db_stats_properties )とページあたりの大量の空白(つまり、sys.dm_db_index_physical_stats dmvの avg_page_space_used_in_percent列 )は、フラグメンテーションのみの場合よりもパフォーマンスの問題に関連します。はい、高度に断片化されたインデックスは、より多くの read-aheads を生成し、通常、古くなった統計と断片化と組み合わされたページあたりの空白のレベルが高くなりますが、断片化はクエリプランの最適化や方法に直接関係していませんディスクからインデックスをロードする多くのメモリが実際に消費します。 クエリプランは、統計情報メモリーフットプリントの膨張により影響を受け、余白が増えます。たとえば、99%が断片化されているが、平均が5%未満のインデックス。空白と最新の統計情報は、統計情報が古くなっているために実行計画が悪い場合や、大量のメモリが原因でメモリに完全に収まらないほどインデックスが頻繁にページングされている場合と比較して、大幅なパフォーマンスの問題を引き起こしていない可能性があります。ページごとに存在する空白の数。

断片化が本当に問題である場合ALTER INDEX ... REORGANIZE コメントでDan Guzmanが特定したステートメント。これは、REBUILD操作のように合理化されたインデックスを作成しませんが、断片化を減らします。ここで重要なのは、データベースで使用率の低いウィンドウを特定して実行することです。これは15分または数時間になる可能性がありますが、明らかに長ければ長いほど良いですが、ここで重要なのは、この操作はロールバックせず、実行中に中断しても、進行状況を保持することです。

断片化が解消された完璧な世界でこのテーブルでパーティション分割を利用する方が理にかなっている場合Azure SQL Databaseテーブルのパーティション分割を許可しており、Microsoftはいくつかの概要を示したすばらしい記事 Azure SQLデータベースのパーティション分割戦略 を提供しています。データが非揮発性である場合、パーティション化することでメンテナンスの必要性を減らすことができます。また、 Table Compression と組み合わせると、ストレージ全体のフットプリントも削減できる可能性があります。 Alberto Murilloの 以前の回答 は、データリージョンに基づいて Horizo​​ntal Partitioning を利用することを示唆しています。このアプローチは、データが次の代わりにリージョン固有になるため、メンテナンスウィンドウを作成するのに役立ちます。グローバル。

パーティション分割テーブルへの移行は、メンテナンスウィンドウがないため簡単ではありませんが、 Maria Zakourdaev で概説されているアプローチを利用できる場合があります Partitioned Views over現在のテーブルの上部と新しいパーティション分割テーブルを使用して、将来のデータのパーティション分割を開始します。時間がたつにつれて(そしてできれば古いデータが削除されたら)、最終的にはパーティションテーブルに完全に移行できます。繰り返しますが、私はあなたのデータやアプリケーションを知りませんが、おそらくこのアプローチはあなたが採用できるものです。

9
John Eisbrener

まず、断片化が重要かどうかを検討することが重要です。

クエリが単一行シークのみを実行している場合、断片化にまったく気付かない可能性があります。最近のSANでは、SANレベルのキャッシュにより、物理IOが十分に高速になり、断片化が問題にならない場合があります。 SSDでは、フラグメント化されたインデックスをスキャンすることによりランダムにIOパターンが発生すると、実際には非フラグメント化よりもbetterパフォーマンスが向上する可能性がありますデータ。

多くの場合、インデックスを再構築するとパフォーマンスの問題が修正されたことに人々は気づきます。インデックスを再構築すると、新しい統計も構築されます。実際の修正は、インデックスを再構築するのではなく、新鮮な統計である場合があります。 UPDATE STATISTICS...WITH FULLSCANは、同じパフォーマンスの問題を解決するための、より安価で、より速く、より邪魔にならない方法かもしれません。

断片化が原因で問題が発生していない場合は、実際の利益を得るために多大な時間と労力を費やしている可能性があります。

次に、2種類の断片化があります。

  1. 物理的な断片化。これは、ほとんどの人が断片化について考えるときに考えることです。ページは順不同であり、再注文する必要があります。 scanningインデックスの場合、このタイプの断片化が問題になることがあります。これがphysical読み取りのパフォーマンスに最大の影響を与えることに一般的に気づきました。 sys.dm_db_index_physical_statsの結果を表示している場合、この数値はavg_fragmentation_in_percent列です。

  2. 低密度の断片化。この断片化は、データで部分的にのみ満たされたページによって引き起こされます。データが必要以上のページに分散しているため、データの密度が低い。その結果、データが必要以上に多くのページに分散されるため、データの読み取りにはより多くのIOが必要になります。これは、論理読み取りと物理読み取りの両方に影響を与える可能性があります。 sys.dm_db_index_physical_statsの結果を表示している場合、この数値はavg_page_space_used_in_percent列です。この列は、SAMPLEDまたはDETAILEDモードを使用する場合にのみ入力されます。

それであなたはそれについて何をしますか:

物理的な断片化:単に[avg_fragmentation_in_percent]の高い数値を追跡している場合は、本当に時間を無駄にしていないか検討してください。実際のクエリのパフォーマンスが低下していることを確認し、テスト環境を使用して、断片化を解消することで問題が修正されていることを確認します。

ALTER INDEX...REORGANIZEを実行すると、物理的な断片化に対処できます。 REORGANIZE操作はオンラインで、ページを1つずつ移動して、物理的な順序に再編成します。 REORGANIZEステートメントを途中で強制終了すると、すでに実行された作業は維持されます。現在移動中の1ページだけがロールバックされます。非常に断片化された大きなテーブルでREORGANIZEを実行すると、トランザクションログ領域の合計がさらに必要になる可能性があり、完全復旧モードでは、大量のトランザクションログバックアップが生成される可能性があります。また、断片化されたインデックスをREORGANIZEするよりもREBUILDするほうが時間がかかる場合があります。

REBUILDではなく、高度にフラグメント化されたインデックスに対してREORGANIZEを実行するというアドバイスがよくあります。これは、ゼロから再構築する方が効率的だからです。ただし、再編成は「よりオンライン」な操作になる可能性があり、高度にフラグメント化されたインデックスの場合でも推奨される場合があります。

低密度の断片化REORGANIZEでは修正できません。 ALTER INDEX...REBUILDを実行することによってのみ修正できます。 ONLINE=ONを使用してインデックスを作成すると、ブロッキングを最小限に抑えることができます。ただし、REBUILDは、古いインデックスを新しいインデックスと交換するために、しばらくロックをかける必要があります。非常にビジーなシステムでは、この排他ロックを取得することが問題になることがあります。 sp_whoisactive のようなものを使用してこの問題が発生しているかどうかを確認して、再構築中のブロッキングを調べ、ロックと待機の詳細を確認する必要があります。 WAIT_AT_LOW_PRIORITYオプションを使用すると、使用率が低くなる期間があり、そのロックを達成するのに十分なほどアクティビティが低下したときに、再構築によってこのスワップが発生する可能性がある場合に役立ちます。実行時間の長いREBUILD操作も、実行時間の長いオープントランザクションになることに注意してください。長時間実行されているオープントランザクションには、トランザクションログの使用/再利用に関連する独自の問題が発生する可能性があります。ミラーリングまたは可用性グループを使用している場合は、セカンダリレプリカでのトランザクションログのやり直しに関する考慮事項もあります。

4
AMtwo

お知らせ

このコメントの後:

コピー中に挿入された行は失われます。テーブルをロックしてこれを防止したい場合は、OPが彼の質問で述べたのと同じ問題が発生します。また、200Gbは無料で提供されません:-) – Marco Sep 5 '17 at 11:18

...私はこのアプローチがうまくいかないことを理解しています。

この答えは、しないの例として残しておきます。


Azure DBに200 GB以上の空き容量がある場合、データをまったく新しいテーブルにコピーしてそこに注文することで、「再構築」をこっそりと行うことができます。

試してください:

  • LiveTableを空のNewTableにスクリプト化する
  • LiveTableNewTableにコピーする
  • LiveTableの名前をOldTableに変更
  • NewTableの名前をLiveTableに変更

明らかに、LiveTableではなくyourテーブルの名前を使用します。

2
Oreo

理想的には、インデックスが適切に設計されていれば、ロックメカニズムをいじる必要はありません。

クラスタ化インデックスをデフラグするにはロックを受け入れる必要があるように思えます。これが再び発生する可能性が高い場合は、クラスター化インデックスの再設計を検討してください(狭く、一意で、静的で、増加し続ける必要があります)。

使用しているSQL Serverのバージョンはわかりませんが、2012年に次のことを試すことができます。

  • SET DEADLOCK_PRIORITY LOW-これは、インデックスの再構築がデッドロックの被害者である場合、または発生した場合に、デッドロックの被害者になることをエンジンに通知します。

  • MaxDOP = 1-MaxDOP値は、インデックスを作成するために並列で使用される論理CPUの総数を制限します(2005以降-Enterpriseエディションのみ)。

ページ/行ロックの構成を変更することもできますが、私はテストなしではそれを行いません。特にインデックスが不適切に設計されている場合は、ロックを悪化させるだけです。

2014年以降、基本的に他のセッションの続行とオンラインインデックス操作の待機をエンジンに通知する次のオプションがあります。

(WAIT_AT_LOW_PRIORITY (MAX_DURATION = 1 MINUTES, ABORT_AFTER_WAIT = SELF))
1
GPep

私は上記のオレオと同じアプローチを使用して大成功を収めました!不足しているのは、データをコピーして最後に名前を変更した後で更新スクリプトを実行する必要があることだけです。

更新は次のようになります。

Insert from OldTable into LiveTable
  Where not exists (select From OldTable Where LiveTable.Key = OldTable.Key)

KeyがIdentity列である場合は、少し異なるアプローチを使用する必要があります。

0
SBB