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


13

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

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

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

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

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


4
@Techyは、高度な断片化があっても、REORGANIZEリーフページの断片化とのようなコンパクトなスペースを削減しますがREBUILD、効率は低下します。大きいサイズは断片化によるものですか?曲線因子とは何ですか?
Dan Guzman

断片化の原因を知っていますか?再構築後どのくらいでスクエア1に戻りますか?テーブルの詳細を投稿できますか?
pacreely

2
@Techy私はあなたのコメントに基づいていくつかの追加情報を追加するために質問を編集しました。質問にテーブルの定義も含めておくと、「[オンラインで再構築]しているときでもテーブルがロックされます」に関連する詳細情報が含まれていると便利です。あなたはどんな種類の待機を見ていますか?
AMtwo 2017

回答:


9

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

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

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

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

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


4

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

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

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

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

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

  1. 物理的な断片化。これは、ほとんどの人が断片化について考えるときに考えていることです。ページは順不同であり、再注文する必要があります。インデックスをスキャンする場合、このタイプの断片化が問題になることがあります。一般的に、これが物理読み取りのパフォーマンスに最大の影響を与えることに気づきました。からの結果を表示している場合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...REORGANIZEREORGANIZE操作はバック物理的な順序にそれらを再編成するときにページ1を動かし、オンラインです。あなたは殺す場合はREORGANIZE、現在、ロールバックされます移動して1ページのみ-を通じて声明の途中、すでに維持されて実行されたすべての作業を。やってREORGANIZE非常に断片化された大きなテーブルの上には、より多くの合計トランザクション・ログ・スペースを必要とすることができ、かつ完全復旧モードでトランザクションログのバックアップを大量に生成することがあります。またREORGANIZE、高度にフラグメント化されたインデックスは、インデックスよりも時間がかかる場合がありますREBUILD

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

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


低密度の断片化(別名「内部断片化」)は、しばしばによって修正されますREORGANIZEBOLから:「再編成するとインデックスページも圧縮されます。」まあ、インデックスの現在のFILLFACTORがあなたの求める密度を許す限り。
グレンジャー、

2

通知

このコメントの後:

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

...このアプローチがうまくいかないのはわかります。

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


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

試してください:

  • LiveTable空にあなたのスクリプトNewTable
  • コピーLiveTableにはNewTable
  • に名前LiveTableを変更するOldTable
  • に名前NewTableを変更するLiveTable

明らかに、ではなくテーブル名を使用してくださいLiveTable


オレオ、私はあなたと同じアプローチを使うでしょう。コピー中に行が挿入されている場合でも、NewTableの名前がLiveTableに変更されていれば、後で行を追加できます。ここで回避する主な問題は、ダウンタイムの延長です。それをbcpすることもできます(i / oコピー先)。それはそれほど悪い考えではないので、反対投票も理解できません:-)
Koen D

1

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

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

使用している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))

0

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

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

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

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


オレオの答えの下で述べたように、彼の方法は動作しません、まだ元のテーブルに追加されているデータがある場合、あなたは運動しの目的に反しない限り、元のテーブルロック
トムVを- topanswers.xyzしてみてください

-2

シャーディングを使用して、データベースのデータを地理的に分散してみてください。これにより、地理的な場所ごとに異なるメンテナンスウィンドウを識別できるようになり、メンテナンスにかかる時間が短縮されます。これにより、パフォーマンスも向上します。これについて詳しく知ることができます記事をください。データベースが大きくなるのを待たないでください。

24時間年中無休で接続されている大規模なデータベースとユーザーでは、更新に必要な統計(sp_updatestats)のみをインデックス再編成および更新して、メンテナンスに必要な時間とユーザーへの影響を最小限に抑える必要があります。

お役に立てれば。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.