次の方法で共有


I/O パフォーマンス分析 - Azure VM 上の SQL Server

適用対象:Azure VM 上の SQL Server

この記事では、Azure Virtual Machines (VM) 上の SQL Server の I/O パフォーマンスを分析して、仮想マシンとデータ ディスクの制限を超えた結果として発生する問題を見つける方法について説明します。

概要

さまざまなツールを使用して SQL Server のパフォーマンスの問題のトラブルシューティングを行うことができますが、Azure VM でこれを効果的に行うには、ホスト レベルと SQL Server インスタンスの両方で何が起こっているかを理解することが重要です。多くの場合、ホスト メトリックと SQL Server ワークロードの関連付けが困難になる可能性があります。 Azure VM 上の SQL Server を使用すると、仮想マシンとデータ ディスクの制限を超えることによる IOPS (1 秒あたりの入力/出力) とスループット調整に起因するパフォーマンスの問題を簡単に特定できます。

問題を示すパフォーマンス メトリックと、それに対処するための潜在的な手順は Azure portal にあり、Azure CLI を使用してクエリを実行できます。

Azure portal の SQL 仮想マシン リソースの [ストレージ] ウィンドウは、次の場合に役立ちます。

メトリックについて

[I/O 分析] タブは Azure メトリックに基づいてディスク待機時間、VM またはディスク I/O 調整を識別します。 Azure メトリックは 30 秒ごとにサンプリングされ、1 分ごとに集計されます。

システムは、調整とディスク待機時間を監視します。 一部の調整は想定されており、ディスクの待ち時間がない限り無視されます。 連続する 5 分間に 500 ミリ秒を超えるディスク待ち時間が発生した場合、システムは次の処理を行います。

  • パフォーマンス メトリックをさらに詳しく調べる
  • 調整されたリソースを識別する
  • 考えられる根本原因と軽減手順を提供する

次の表では、原因となっている調整の問題を特定するために使用される Azure メトリックについて説明します。

Azure メトリック メトリックの説明 原因となっている状態 I/O 調整の結論
ディスク待機時間 (プレビュー) 監視期間中のデータ ディスクの IO を完了するための平均時間。 値はミリ秒単位で示されます。 >連続する 5 分間で 500 ミリ秒 システムが潜在的な調整をさらに調査するための待ち時間の問題があります。
VM のキャッシュあり IOPS の消費率 仮想マシンのキャッシュあり IOPS の上限に対する完了済みの合計 IOPS で計算された割合。 >= 連続する 5 分間で 95% VM の調整があります。 SQL 仮想マシンで実行されているアプリケーションは、仮想マシンで使用可能な最大キャッシュ IOPS 容量をすべて利用しています。アプリケーションのストレージ要求が、仮想マシンの基になるストレージ構成によって提供されるキャッシュされた IOPS を超えています。
VM キャッシュ帯域幅の消費割合 仮想マシンのキャッシュあり最大スループットに対する完了済みの合計ディスク スループットで計算された割合。 >= 連続する 5 分間で 95% VM の調整があります。 SQL 仮想マシンで実行されているアプリケーションは、データ転送に使用可能なキャッシュされたディスク帯域幅の最大値を利用しています。アプリケーションのデータ転送要求が、仮想マシンの基になるストレージ構成によって提供されるキャッシュされた帯域幅リソースを超えています。 
VM のキャッシュなし IOPS の消費率 仮想マシンのキャッシュなし IOPS の上限に対する仮想マシンの完了済みの合計 IOPS で計算された割合。 >= 連続する 5 分間で 95% VM の調整があります。 SQL 仮想マシンで実行されているアプリケーションは、仮想マシンで使用可能なキャッシュされていない最大 IOPS 容量を利用しています。アプリケーションのストレージ要求が、仮想マシンの基になるストレージ構成によって提供されるキャッシュされていない IOPS リソースを超えています。
VM のキャッシュされていない帯域幅の消費率 仮想マシンのプロビジョニングされた最大スループットに対する仮想マシンの完了済みの合計ディスク スループットで計算された割合。 >= 連続する 5 分間で 95% VM の調整があります。 SQL 仮想マシンで実行されているアプリケーションは、データ転送に許容されるキャッシュされていないディスク帯域幅の最大値を利用しています。アプリケーションのデータ転送要求が、仮想マシンの基になるストレージ構成によって提供されるキャッシュされていない帯域幅リソースを超えています。
データ ディスク IOPS の消費率 プロビジョニングされたデータ ディスク IOPS に対する完了済みのデータ ディスク IOPS で計算された割合。 >= 連続する 5 分間で 95% データ ディスクの調整があります。 SQL 仮想マシンで実行されているアプリケーションが、プロビジョニングされたデータ ディスクの IOPS 制限に達しています。アプリケーションのストレージ要求が、選択したディスク構成のパフォーマンス機能を超えています。
データ ディスク帯域幅の消費率 プロビジョニングされたデータ ディスクのスループットに対する完了済みのデータ ディスクのスループットで計算された割合。 >= 連続する 5 分間で 95% データ ディスクの調整があります。 SQL 仮想マシンで実行されているアプリケーションが、プロビジョニングされたデータ ディスクの IOPS 制限に達しています。アプリケーションのストレージ要求が、選択したディスク構成のパフォーマンス機能を超えています。

I/O 分析結果

過去 24 時間のパフォーマンス メトリックの分析に基づいて、I/O 分析では次のことが判断されます。

  • 調整なし
  • VM レベルの I/O 調整
  • ディスク レベルの I/O 調整

I/O 調整の問題なし

パフォーマンスの問題が発生してもディスクの待機時間がない場合、パフォーマンスの問題は I/O 調整の問題が原因ではありません。 他の領域を調査する必要があります。 Azure VM 上の SQL Server のベスト プラクティス チェックリストを使用して、システムが効率的に構成されていることを確認したり、SQL Server のパフォーマンスのトラブルシューティングに役立つリンクを見つけたりすることができます。 SQL ベスト プラクティス評価 機能を有効にすると、SQL Server VM の推奨事項の完全な一覧が表示されます。

VM レベルの I/O 調整の問題

Azure Virtual Machines はクラウドベースのコンピューティング リソースであり、さまざまなワークロードのさまざまなシリーズとサイズで提供され、それぞれ異なる機能とパフォーマンス特性を備えています。 SQL Server ワークロードの場合、一般に、SQL Server ワークロードに推奨されるシリーズは、Ebdsv5、M、Mv2 シリーズなどのメモリ最適化シリーズです。

VM のサイズによって、SQL Server インスタンスで使用できる vCPU、メモリ、ストレージの数が決まります。 ストレージと比べると、お客様が仮想マシンのサイズを変更し、アプリケーション リソースのニーズに基づいて VM をスケールアップおよびスケールダウンすることは比較的簡単です。 IOPS とスループットが VM レベルで調整される可能性があるため、パフォーマンスのニーズとワークロードのコストに基づいて適切な VM サイズを選択します。

Azure に移行する場合は、 SKU の推奨事項 ツールを使用して、現在の SQL Server の構成と使用状況を分析し、Azure のワークロードに最適な VM サイズを提案できます。

次の Azure メトリックを使用して、VM によって課される制限を超えないようにワークロードが調整されていることを判断します。

  • VM のキャッシュされた IOPS の消費率
  • VM のキャッシュされた帯域幅の消費率
  • VM のキャッシュされていない IOPS の消費率
  • VM のキャッシュされていない帯域幅の消費率

VM の調整に関する重要なポイントを次に示します。

  • VM シリーズ内の仮想マシンのサイズを変更することで、メモリ、仮想コア、スループット、IOPS を増やすことができます。
  • データ ディスクの数がターゲット VM サイズの最大データ ディスク制限を超えるほど VM サイズを減らすことはできません。
  • 調整パターンを決定することが重要です。 たとえば、ワークロードのチューニングによって調整の急増が頻繁に発生する可能性があります。一方、持続的なスパイクは、基になるストレージがワークロードを処理できないことを示している可能性があります。

ディスク レベルの I/O 調整の問題

SQL 仮想マシンのお客様にとって、ストレージの調整は仮想マシンのサイズを変更するよりも難しいため、ストレージは最適化されたパフォーマンスのために適切に構成する最も重要な側面です。 たとえば、Premium SSD ディスクの IOPS またはスループットを向上させるために変更を加える場合は、新しい記憶域プールを作成する必要があります。 そのため、デプロイ後のパフォーマンスの問題を回避するために、計画フェーズ中に価格とパフォーマンスの両方のためにストレージ構成を最適化することが重要です。

次の Azure メトリックを使用して、ディスクによって課される制限を超えないようにワークロードが調整されていることを確認します。

  • データ ディスク IOPS の消費率

  • データ ディスク帯域幅の消費率 ディスク レベルの I/O 調整に関する次の重要なポイントを考慮してください。

  • データ ディスクは、SQL Server のパフォーマンスにとって重要です。 データ ディスクに SQL Server データ (.mdf) ファイルとログ (.df) ファイルを配置することをお勧めします。

  • データ ディスク レベルで調整を行う場合は、読み取りキャッシュを有効にします (使用可能な場合)。

データ ディスク IOPS の消費率

データ ディスクの IOPS 消費率 メトリックは、ディスク レベルでの IOPS 消費量を測定します。 一般に、高 IOPS のニーズは、高トランザクションの OLTP ベースのアプリケーションとワークロードに関連付けられています。   次のシナリオまたは条件は、データ ディスクの IOPS の制限を超える可能性があります。

  • 高トランザクション ワークロード (IOPS): アプリケーションが頻繁に読み取りと書き込みの操作を伴う大量のデータベース トランザクションを処理している場合、割り当てられた IOPS をすぐに消費する可能性があります。 
  • 非効率的なクエリ: SQL クエリまたはデータ取得操作が最適化されていないと、I/O アクティビティが過剰になり、予想よりも多くの IOPS が消費される可能性があります。 
  • 同時ユーザー: 複数のユーザーまたはセッションが同時にデータベースにアクセスし、I/O 要求を生成している場合、累積効果によって IOPS の制限に達する可能性があります。 
  • リソースの競合: 基になる物理インフラストラクチャが他のテナントまたはワークロードと頻繁に共有されている場合、仮想マシンで使用可能な IOPS に影響を与える可能性があります。 
  • 一時的なスパイク: バッチ処理やデータ移行などのワークロードの一時的な急増により、割り当てられた IOPS を超える I/O 需要が急激に増加する可能性があります。 
  • 小さいディスク サイズ: プロビジョニングされたデータ ディスク サイズが比較的小さい場合、IOPS 容量が制限される可能性があります。 個々の小さいディスクの IOPS 制限は低く、アプリケーションの要求がこの制限を超えると、"Data Disk IOPS Consumed Percentage" は 100% に達します。 
  • 不十分なディスクの種類: I/O 集中型アプリケーションのパフォーマンスの低いディスクの種類 (Standard HDD など) を選択すると、IOPS の制限が発生する可能性があります。 
  • 最適化されていないディスク ストライプ サイズ: ストレージ構成がワークロード用に最適化されていない場合、最適でない IOPS パフォーマンスにつながる可能性があります。 

データ ディスクの IOPS 制限を超えないようにするには、次の手順を検討してください。

  • 不要な I/O 操作を最小限に抑えるために、SQL クエリとデータベース設計を最適化します。 
  • アプリケーションの IOPS 要件に一致する適切なディスクの種類 (Standard SSD または Premium SSD) を選択します。 
  • ディスク サイズを大きくして、使用可能な IOPS 容量を増やします。 
  • RAID 構成を使用して、複数のデータ ディスクに I/O を分散します。 

データ ディスク帯域幅の消費率

データ ディスク帯域幅の消費率 Azure メトリックは、ディスク レベルでの帯域幅使用率を測定します。 一般に、高スループットのニーズは、データ ウェアハウス、データ マート、レポート、ETL、およびその他のデータ分析ワークロードに関連しています。

次のシナリオまたは条件は、データ ディスクの帯域幅の制限を超える可能性があります。

  • 大きなデータ転送: ディスクと SQL データベース間の大規模なアプリケーション データ転送が頻繁に行われると、使用可能なデータ ディスク帯域幅がすぐに消費される可能性があります。 
  • 一括データ読み込み: 一括データの挿入、更新、またはインポートに関連付けられているディスク転送アクティビティは、高帯域幅の消費につながる可能性があります。 
  • データ ウェアハウスまたは分析: 大量のデータ ウェアハウス、分析処理、またはレポートを含むアプリケーションでは、大量のデータ移動が発生し、帯域幅の制限を超える可能性があります。
  • 高いデータ冗長性テクノロジ/レプリケーション: 関連するデータ コピーでは、ディスク ベースのレプリケーション、データ ミラーリング、またはその他の冗長性メカニズムが使用され、帯域幅の飽和に寄与する可能性があります。 
  • データのバックアップと復元: 頻繁なデータ バックアップ、スナップショット、または復元プロセスでは、大量のデータ ディスク帯域幅が消費される可能性があります。 
  • 並列クエリ実行: 大規模なデータ スキャンまたは結合を伴う並列クエリは、帯域幅の使用率につながる大量のデータ移動につながる可能性があります。 
  • ネットワーク トラフィックの昇格: 仮想マシンとその他のリソース間のデータ転送などの高いネットワーク アクティビティは、データ ディスクの帯域幅の可用性に間接的に影響する可能性があります。 
  • 不十分なディスクの種類: データ転送要件が高いアプリケーションでパフォーマンスの低いディスクの種類を選択すると、帯域幅の制限を超える可能性があります。 
  • 同時データ集中型操作: 複数の同時実行プロセスまたはセッションが同じディスク上のデータにアクセスして転送すると、帯域幅の制限に達する可能性があります。 
  • 最適化されていないクエリまたは ETL プロセス: 最適化されていない SQL クエリまたは抽出、変換、読み込み (ETL) プロセスは、過剰なデータ移動を引き起こす可能性があります。その結果、帯域幅が過剰に消費される可能性があります。 

データ ディスクの帯域幅制限を超えないようにするには、次の手順を検討してください。

  • データ転送操作を最適化して、不要なデータ移動を最小限に抑えます。 
  • Premium SSD や Premium SSD v2 などの帯域幅容量を提供する、パフォーマンスの高いディスクの種類の使用を検討してください。
  • パーティション分割やシャーディングなどの手法を使用して、複数のディスクにデータを分散します。
  • クエリとデータ処理を最適化して並列化し、データ移動を減らします。
  • 圧縮と効率的なデータ ストレージ メカニズムを使用して、転送されるデータの量を減らします。
  • パフォーマンス メトリックを監視し、必要に応じてストレージ構成をスケールアップします。 Premium SSD v2 を使用すると、必要に応じて IOPS とスループットを必要に応じてスケーリングできます。
  • パフォーマンス メトリックを定期的に監視および分析して、IOPS の制限の原因を特定し、SQL 仮想マシンのストレージ パフォーマンスを最適化するための適切なアクションを実行することが重要です。

ヒント

パフォーマンス メトリックの定期的な監視、データ転送操作のチューニング、ディスク構成の最適化により、SQL 仮想マシンのデータ ディスクのパフォーマンスが制限を超えることなく最適な状態を維持できるようにすることができます。

調整なしの待機時間

調整なしの待機時間とは、ストレージ システムが最大 IOPS またはスループットの制限に達していない場合でも発生するデータ アクセスまたは処理の遅延を指します。 Azure VM の待機時間は、オペレーティング システムの I/O スタック、SQL Server 処理、ネットワーク オーバーヘッド、ハイパーバイザー スケジューリングなど、さまざまなソースから発生する可能性があります。 待機時間の原因を特定することは、Azure VM での SQL Server のパフォーマンスを最適化するために重要です。

調整なしで待機時間が検出された場合、[ストレージ] ウィンドウの [I/O 分析] タブに次の警告が表示されます。Warning: High disk latency detected without throttling

調整なしで待機時間が発生する可能性がある原因を次に示します。

  • CPU 使用率が高い: CPU の負荷が高いと、CPU が暗号化、圧縮、クエリ実行などのタスクでビジー状態であるため、I/O 操作が遅くなる可能性があります。 これは、コア数が少ない VM では特に一般的です。 CPU サイクルが使用できない場合、I/O 要求は処理されるまで長く待機でき、ストレージの制限に達しない場合でも待機時間が長くなります。 たとえば、データの暗号化など、CPU を集中的に使用するプロセスを実行している VM では、SQL Server I/O 操作が遅くなり、クエリの応答時間が遅くなる可能性があります。
  • VM のメモリ不足: SQL Server では、データをキャッシュするためにメモリが重要であるため、ディスク I/O の必要性が軽減されます。 メモリが制約されている場合、SQL Server ではディスクから読み取る頻度が高くなり、待機時間が長くなる可能性があります。 これは、メモリが少ない VM や、メモリ集中型のバックグラウンド プロセスがリソースを競合する場合に特に関連します。 これにより、IOPS の制限に達していない場合でも、ワークロードを処理するために必要なディスク操作の頻度が増えるので、ストレージの待機時間が間接的に長くなる可能性があります。
  • バックグラウンド プロセス: VM 上のその他のプロセス (ウイルス対策ソフトウェア、バックアップ、メンテナンス タスク (Windows Update など) など) は、CPU、メモリ、ディスク I/O リソースを消費し、SQL Server の操作を遅らせる可能性があります。 非効率的なフィルター ドライバーは、この効果を悪化させる可能性があります。 これらのプロセスは、システム リソースの SQL Server と競合するため、ストレージの待機時間として表示される I/O 遅延が発生します。 たとえば、多数のファイルを同時に読み取るウイルス対策スキャンでは、SQL Server で使用できるディスク帯域幅が減らされる可能性があります。このため、データベース トランザクションの待機時間が長くなります。 さらに、適切なウイルス対策の除外がない場合、主にディスク I/O の増加、フィルター ドライバーの干渉、リソースの競合によって、Azure VM 上の SQL Server で調整を行うことなく待機時間の問題が発生する可能性があります。
  • 低階層ストレージの使用量: Premium SSD や Ultra Disks ではなく Standard HDD などの下位レベルのストレージ オプションを選択すると、IOPS 制限に達しなくても、これらのディスク固有の設計によりベースライン待機時間が長くなります。 コスト効率に優れた低層ストレージは、パフォーマンス重視の SQL Server ワークロードには最適化されていないため、データ アクセスが遅くなります。 たとえば、Standard HDD を使用してコストを節約しているお客様は、ディスクの待機時間が自然に長いため、クエリのパフォーマンスが低下する可能性があります。
  • 不適切なストレージ構成: SQL Server ワークロード用に最適化するようにストレージを構成しないと、調整なしで待機時間が発生する可能性があります。 たとえば、ディスク キャッシュの設定が正しくないと、パフォーマンスが低下する可能性があります。 Azure VM 上の SQL Server で Premium SSD v1 を使用する場合は、データ ディスクの読み取り専用キャッシュを有効にし、ログ ディスクのキャッシュを無効にすることをお勧めします。 キャッシュが正しく構成されていないと、読み取り操作または書き込み操作が遅くなる可能性があります。 たとえば、SQL Server データ ファイルをホストするデータ ディスクで読み取りキャッシュを無効にすると、読み取り負荷の高いワークロードの効率が低下し、待機時間が長くなります。
  • SQL Server データベースの競合: 非効率的なクエリ (インデックス付き参照ではなく完全なテーブル スキャンなど) や SQL Server 内でのロックの競合により、I/O 要求が増加したり、データ アクセスが遅延したりする可能性があり、これはストレージ待機時間として現れます。 アプリケーション レベルの問題では、特にトランザクション ワークロードで一般的な小規模でランダムな I/O パターンを使用して、制限を超えることなくストレージ サブシステムを負担する可能性があります。 たとえば、大きなデータセットに対して完全なテーブル スキャンを実行する最適化が不十分なクエリでは、ディスクから過剰なデータが読み取られ、インデックス付きクエリと比較して I/O の読み込みと待機時間が増加します。

調整なしで待機時間が発生する場合は、待機時間に対処するために次の手順を検討してください。

  • CPU 使用率の監視と管理: Azure Monitor や Resource Monitor などのツールを使用して CPU 使用率を追跡します。 CPU 負荷が高い場合は、SQL クエリを最適化するか、仮想コアが多い VM にアップグレードします。
  • メモリ使用量の監視: E シリーズや M シリーズなど、より高いメモリ対仮想コア比の VM サイズを使用して、VM サイズに SQL Server ワークロードに適したメモリがあることを確認します。 パフォーマンス モニターまたは Azure Monitor を使用してメモリ使用量を監視し、圧力ポイントを特定します。 必要に応じて、メモリのスケールアップを検討してください。
  • バックグラウンド タスクを慎重にスケジュールする: リソースが多いタスク (バックアップやウイルス対策スキャンなど) をピーク時間外に実行して、SQL Server とリソースの競合を回避します。
  • 適切なストレージ層を選択する: 下位層のストレージ (Standard HDD など) がパフォーマンスニーズを満たしているかどうかを評価します。 重要な SQL Server ワークロードの場合は、待機時間を最小限に抑えるために Premium SSD または Ultra Disks を選択します。
  • キャッシュを正しく構成する: Premium SSD (v1) の場合は、データ ディスクの読み取り専用キャッシュを設定し、ログ ディスクのキャッシュは設定しません。 Premium SSD v2 と Ultra Disks ではキャッシュがサポートされていないため、VM またはストレージの変更後の設定を確認します。
  • SQL Server のパフォーマンスの最適化: クエリを確認して調整し、I/O の需要を減らします。 インデックスを実装し、テーブル全体のスキャンを回避し、ロックの競合を解決して効率を向上させます。 ベスト プラクティス分析 機能を使用して、パフォーマンスを向上できる構成オプションを特定します。
  • ウイルス対策の除外が正しいことを確認する: 適切な除外を実装し、負荷の下でテストし、スキャンを適切にスケジュールすることで、待機時間の問題を軽減し、セキュリティ バランスで最適なパフォーマンスを確保できます。

ストレージ システムが適切に構成されていないとパフォーマンスの問題が発生する可能性があるため、Azure portal の [ストレージ] ウィンドウを使用して、SQL ベスト プラクティス評価規則のディスク固有のサブセットを実行して、Azure VM 上の SQL Server のストレージ構成の問題を特定できます。 SQL のベスト プラクティス機能は、SQL Assessment API に基づいています。

GitHub で、推奨事項の完全な一覧を表示できます。 GitHub のルールの ID 列でフィルター処理すると、Azure portal の SQL 仮想マシン リソースの [ストレージ] ウィンドウの [I/O 構成のベスト プラクティス] タブで検証された SQL VM ディスク構成規則を確認できます。

  • AzSqlVmSize
  • AzDataDiskCache
  • AzDataDiskStriping
  • AzDataOnDataDisks
  • AzDbDefaultLocation
  • AzDiskColumnCount
  • AzErrorLogLocation
  • AzPremSSDデータファイル
  • AzTempDbFileLocation (一時的なデータベースファイルの場所)
  • AzTranLogDiskCache
  • NtfsBlockSizeNotFormatted
  • メモリ内のロックされたページ

[I/O 関連のベスト プラクティス] タブで、評価を実行 して構成の評価を開始します。完了するまでに数分かかります (データベースとオブジェクトが多数ある場合を除く)。 または、利用可能な最新の結果のタイムスタンプが表示された場合は、最新の結果を取得を使用して、以前の評価の結果を確認できます。

PowerShell を使用して I/O を分析する

I/O 分析 PowerShell スクリプトを使用して、SQL Server VM の I/O パフォーマンスを分析することもできます。

# Enter parameters
$subscriptionId = Read-Host "<Subscription ID>"
$resourceGroup = Read-Host "<Resource Group>"
$vmName = Read-Host "<Virtual machine name>"

# Set resource details
$resourceType = "Microsoft.Compute/virtualMachines"
$resourceId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/$resourceType/$vmName"

# Get Azure access token
$accessToken = az account get-access-token --query accessToken -o tsv

# Invoke Azure Monitor Metrics API
function Get-Metrics {
    [CmdletBinding()]
    param (
        [string]$accessToken,
        [string]$resourceId,
        [string]$metricNames,
        [string]$apiVersion = "2023-10-01"
    )
    try {
        $startTime = (Get-Date).AddHours(-24).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        $endTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        $timespan = "$startTime/$endTime"
        Write-Verbose "Evaluating timespan: $timespan"
        $uri = "https://management.azure.com$resourceId/providers/Microsoft.Insights/metrics?api-version=$apiVersion&metricnames=$metricNames&aggregation=maximum&interval=PT1M&timespan=$timespan"
        $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json" }
        
        $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
        if ($response) {
            Write-Verbose "API response successfully retrieved."
            return $response
        } else {
            Write-Error "No response from API."
        }
    } catch {
        Write-Error "Error retrieving metrics: $_"
    }
}

# Check if data disk latency violates thresholds
function Check-Latency {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Object]$metrics,

        [Parameter()]
        [int]$latencyThreshold = 500,

        [Parameter()]
        [int]$consecutiveCount = 5
    )
    $violationTimes = @()
    foreach ($metric in $metrics.value) {
        if ($metric.name.value -eq "Data Disk Latency") {
            $count = 0
            foreach ($dataPoint in $metric.timeseries[0].data) {
                if ($dataPoint.maximum -gt $latencyThreshold) {
                    $count++
                    if ($count -ge $consecutiveCount) {
                        $violationTimes += $dataPoint.timeStamp
                        $count = 0  # Reset count after recording a violation
                    }
                } else {
                    $count = 0  # Reset count if the sequence is broken
                }
            }
        }
    }
    if ($violationTimes.Count -gt 0) {
        Write-Verbose "Latency violations detected."
        return @{ "Flag" = $true; "Times" = $violationTimes }
    } else {
        Write-Verbose "No latency violations detected."
        return @{ "Flag" = $false }
    }
}

# Check metrics other than latency to evaluate for throttling
function Check-OtherMetricsThrottled {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Object]$metrics,

        [Parameter()]
        [int]$PercentageThreshold = 90,

        [Parameter()]
        [int]$consecutiveCount = 5
    )
    $violatedMetrics = @()
    foreach ($metric in $metrics.value) {
        $count = 0
        foreach ($dataPoint in $metric.timeseries[0].data) {
            if ($dataPoint.maximum -gt $PercentageThreshold) {
                $count++
                if ($count -ge $consecutiveCount) {
                    $violatedMetrics += @{ "Metric" = $metric.name.localizedValue; "Time" = $dataPoint.timeStamp; "Value" = $dataPoint.maximum }
                    break
                }
            } else {
                $count = 0
            }
        }
    }
    if ($violatedMetrics.Count -gt 0) {
        Write-Verbose "Other metrics violations detected."
    } else {
        Write-Verbose "No other metrics violations detected."
    }
    return $violatedMetrics
}

# Compare times for latency & other throttled metrics. Logs the volations with values & timestamps
function CompareTimes {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Hashtable]$latencyResult,
        
        [Parameter(Mandatory = $true)]
        [Array]$otherMetrics
    )
    foreach ($metric in $otherMetrics) {
        $otherDateTime = [DateTime]$metric["Time"]
        $isWithinFiveMinutes = $false
        $closestLatencyTime = $null
        $closestTimeDifference = [int]::MaxValue

        foreach ($latencyTime in $latencyResult.Times) {
            $latencyDateTime = [DateTime]$latencyTime
            $timeDifference = [Math]::Abs(($otherDateTime - $latencyDateTime).TotalMinutes)
            
            if ($timeDifference -le 5) {
                $isWithinFiveMinutes = $true
                if ($timeDifference -lt $closestTimeDifference) {
                    $closestTimeDifference = $timeDifference
                    $closestLatencyTime = $latencyTime
                }
            }
        }

        if ($isWithinFiveMinutes) {
            if ($otherDateTime -lt $closestLatencyTime) {
                Write-Host "`n $($metric["Metric"]) limit was hit before latency spiked at $closestLatencyTime with value $($metric["Value"]). `n"
            } else {
                Write-Host "`n $($metric["Metric"]) hit its limit with value $($metric["Value"]) at $($metric["Time"])."
                Write-Host "Latency spiked at $closestLatencyTime before $($metric["Metric"]) hit its limit `n"
            }
        } else {
            Write-Host "`n Metric: $($metric["Metric"]) exceeded its threshold with a value of $($metric["Value"]) at $($metric["Time"]), but this was not within 5 minutes of any latency spikes."
        }
    }
}

# Prompt user for latency threshold
$latencyThreshold = Read-Host "Enter Latency Threshold (default is 500)"
if (-not [int]::TryParse($latencyThreshold, [ref]0)) {
    $latencyThreshold = 500 # Use default if invalid input
    Write-Host "No valid input provided. Using Default 500ms for disk latency threshold"
}

# Execute main logic
$latencyMetrics = Get-Metrics -accessToken $accessToken -resourceId $resourceId -metricNames "Data Disk Latency"
$latencyResult = Check-Latency -metrics $latencyMetrics -latencyThreshold $latencyThreshold

if ($latencyResult.Flag) {
    
    # If latency is flagged, check for other metrics. If there is no disk latency, machine is likely not throttled but only at high consumption
    Write-Verbose "Checking the following metrics: Data Disk Bandwidth Consumed Percentage,Data Disk IOPS Consumed Percentage,VM Cached Bandwidth Consumed Percentage,VM Cached IOPS Consumed Percentage,VM Uncached Bandwidth Consumed Percentage,VM Uncached IOPS Consumed Percentage"
    
    $DiskVMMetrics = Get-Metrics -accessToken $accessToken -resourceId $resourceId -metricNames "Data Disk Bandwidth Consumed Percentage,Data Disk IOPS Consumed Percentage,VM Cached Bandwidth Consumed Percentage,VM Cached IOPS Consumed Percentage,VM Uncached Bandwidth Consumed Percentage,VM Uncached IOPS Consumed Percentage"
    
    $additionalMetrics = Check-OtherMetricsThrottled -metrics $DiskVMMetrics
    
    if ($additionalMetrics.Count -gt 0) {
        CompareTimes $latencyResult $additionalMetrics
    } else {
        Write-Host "No metrics violations detected besides latency."
    }
} else {
    Write-Host "No latency issues detected."
}

次のステップ