Azure Monitor でログ クエリを最適化する

Azure Monitor ログでは、Azure Data Explorer を使用して、ログ データを格納し、そのデータを分析するためのクエリを実行します。 これにより、Azure Data Explorer クラスターが作成、管理、保持され、ログ分析ワークロードに合わせて最適化されます。 クエリを実行すると、クエリが最適化され、ワークスペース データを格納する適切な Azure Data Explorer クラスターにルーティングされます。

Azure Monitor ログと Azure Data Explorer は、クエリの自動最適化メカニズムを多数使用しています。 自動最適化によって大幅に処理が促進されますが、ユーザーがクエリのパフォーマンスを飛躍的に向上させることができるケースもいくつかあります。 この記事では、パフォーマンスに関する考慮事項とそれを調整するいくつかの手法について説明します。

ほとんどの手法は、Azure Data Explorer と Azure Monitor ログに直接実行されるクエリに共通です。 Azure Monitor ログに固有のいくつかの考慮事項についても説明します。 Azure Data Explorer の最適化に関するヒントについては、「クエリのベスト プラクティス」を参照してください。

最適化されたクエリは次のようになります。

  • 実行速度が上がり、クエリ実行全体の期間が短縮されます。
  • スロットルまたは拒否される可能性が低くなります。

ダッシュボード、アラート、Azure Logic Apps、Power BI などの繰り返し同時に使用されるクエリには、特に注意を払ってください。 このような場合に効果のないクエリが及ぼす影響はかなり大きくなります。

最適化されたクエリに関する詳細なビデオ チュートリアルはこちらです。

クエリの詳細ペイン

Log Analytics でクエリを実行した後、画面の右下隅にある [クエリの詳細] を選択して、[クエリの詳細] ペインを開きます。 このペインには、クエリのパフォーマンス指標の結果が表示されます。 これらのパフォーマンス指標については、次のセクションで説明します。

Screenshot that shows the Query Details pane in Azure Monitor Log Analytics.

クエリ パフォーマンス指標

実行されるクエリごとに、次のクエリ パフォーマンス指標を使用できます。

  • [合計 CPU]: すべての計算ノードでクエリを処理するために使用された全体的な計算。 これは、計算、解析、データの取得に使用された時間を表します。
  • [処理されたクエリに使用するデータ]: クエリを処理するためにアクセスされたデータ全体。 対象のテーブルのサイズ、使用された期間、適用されたフィルター、参照されている列の数の影響を受けます。
  • [処理されたクエリの期間]: クエリを処理するためにアクセスされた最新データと最も古いデータの差分。 クエリに指定された明示的な時間の範囲の影響を受けます。
  • [処理されたデータの期間]: 現在と、クエリを処理するためにアクセスされた最も古いデータの差分。 これは、データの取得の効率に大きく影響します。
  • [ワークスペースの数]: 暗黙的または明示的な選択に基づいてクエリ処理中にアクセスされたワークスペースの数。
  • [リージョンの数]: 暗黙的または明示的なワークスペースの選択に基づいてクエリ処理中にアクセスされたリージョンの数。 複数リージョンのクエリでは効率がかなり低下し、パフォーマンス指標の対応範囲が部分的になります。
  • [並行処理]:システムにより、このクエリをどの程度、複数ノードで実行できたかを示します。 CPU 使用率が高いクエリにのみ関連します。 特定の関数と演算子の使用による影響を受けます。

合計 CPU

すべてのクエリ処理ノードでこのクエリを処理するために実際に投入されたコンピューティング CPU です。 ほとんどのクエリは多数のノード上で実行されるため、通常、この合計はクエリの実行にかかった期間よりもはるかに大きくなります。

CPU を 100 秒以上使用するクエリは、リソースを過度に消費するクエリと見なされます。 CPU を 1,000 秒以上使用するクエリは、リソースを酷使するクエリと見なされ、スロットルされることがあります。

クエリの処理時間は以下に費やされます。

  • データ取得: 古いデータの取得には、最近のデータの取得よりも時間がかかります。
  • データ処理: データのロジックと評価。

クエリ処理ノードでの所要時間に加えて、Azure Monitor ログが次の時間を費やします。

  • ユーザーの認証と、このデータへのアクセスが許可されていることの確認。
  • データ ストアの検索。
  • クエリの解析。
  • クエリ処理ノードの割り当て。

この時間は、クエリの合計 CPU 時間には含まれません。

CPU 使用率の高い関数を使用する前にレコードを早期にフィルター処理する

クエリのコマンドと関数の中には、CPU 使用率の高いものがあります。 このケースは特に、JSON と XML の解析や複雑な正規表現の抽出を実行するコマンドにあてはまります。 このような解析は、parse_json () または parse_xml () 関数で明示的に行われることもあれば、動的な列の参照時に暗黙的に行われることもあります。

これらの関数では、処理対象の行数に比例して CPU が消費されます。 最も効率的な最適化は、クエリの早い段階で where 条件を追加することです。 これにより、CPU を集中的に使用する関数が実行される前に、できるだけ多くのレコードを除外できます。

たとえば、次のクエリでは厳密に同じ結果が生成されます。 しかし、2 番めのものが最も効率的です。解析前に、where 条件によって多くのレコードが除外されるためです。

//less efficient
SecurityEvent
| extend Details = parse_xml(EventData)
| extend FilePath = tostring(Details.UserData.RuleAndFileData.FilePath)
| extend FileHash = tostring(Details.UserData.RuleAndFileData.FileHash)
| where FileHash != "" and FilePath !startswith "%SYSTEM32"  // Problem: irrelevant results are filtered after all processing and parsing is done
| summarize count() by FileHash, FilePath
//more efficient
SecurityEvent
| where EventID == 8002 //Only this event have FileHash
| where EventData !has "%SYSTEM32" //Early removal of unwanted records
| extend Details = parse_xml(EventData)
| extend FilePath = tostring(Details.UserData.RuleAndFileData.FilePath)
| extend FileHash = tostring(Details.UserData.RuleAndFileData.FileHash)
| where FileHash != "" and FilePath !startswith "%SYSTEM32"  // exact removal of results. Early filter is not accurate enough
| summarize count() by FileHash, FilePath
| where FileHash != "" // No need to filter out %SYSTEM32 here as it was removed before

評価済みの where 句を使用しない

データセットに物理的に存在する列ではなく、評価済みの列に where 句が配置されているクエリでは、効率が低下します。 大規模なデータ セットを処理する場合、評価済みの列に対してフィルター処理を行ってもシステムは最適化されない場合があります。

たとえば、次のクエリでは厳密に同じ結果が生成されます。 しかし、2 番めのものがより効率的です。where 条件が組み込みの列を参照するためです。

//less efficient
Syslog
| extend Msg = strcat("Syslog: ",SyslogMessage)
| where  Msg  has "Error"
| count 
//more efficient
Syslog
| where  SyslogMessage  has "Error"
| count 

場合によっては、フィルター処理が行われる対象はフィールドだけではないため、評価済みの列がクエリ処理エンジンによって暗黙的に作成されます。

//less efficient
SecurityEvent
| where tolower(Process) == "conhost.exe"
| count 
//more efficient
SecurityEvent
| where Process =~ "conhost.exe"
| count 

効果的な集計コマンドおよびディメンションを summarize と join で使用する

max()sum()count()avg() などの一部の集計コマンドは、そのロジックにより、CPU への影響が低くなっています。 その他のコマンドはより複雑で、効率的に実行できるようにするためのヒューリスティックと推定が含まれています。 たとえば、dcount() は HyperLogLog アルゴリズムを使用して、大規模なデータ セットの重複しない値の数について、実際には各値を数えずに精度の高い推定値を提供します。

パーセンタイル関数は、最も近いランクのパーセンタイル アルゴリズムを使用して同様の近似を実行しています。 コマンドのいくつかには、影響を軽減するための省略可能なパラメーターが含まれています。 たとえば、makeset () 関数には、CPU とメモリに大きく影響する、最大セット サイズを定義するための省略可能なパラメーターがあります。

join および summarize コマンドを使用すると、大規模なデータ セットを処理するときに CPU 使用率が高くなる可能性があります。 これらの複雑さは、summarize 内で by として、または join 属性として使用されている列に指定可能な値の数 ("カーディナリティ" と呼ばれます) に直接関連しています。 join および summarize の説明と最適化については、それぞれのドキュメント記事と最適化のヒントを参照してください。

たとえば、次のクエリでは、CounterPath が常に 1 対 1 で CounterName および ObjectName にマップされるため、まったく同じ結果が得られます。 2 番目のクエリは、集計ディメンションが小さいため、より効率的です。

//less efficient
Perf
| summarize avg(CounterValue) 
by CounterName, CounterPath, ObjectName
//make the group expression more compact improve the performance
Perf
| summarize avg(CounterValue), any(CounterName), any(ObjectName) 
by CounterPath

CPU 使用率は、集中的なコンピューティングを必要とする where 条件や拡張列による影響を受ける可能性もあります。 equal ==startswith などの単純な文字列比較では CPU への影響がほぼ同じです。 高度なテキストの一致では、影響がより大きくなります。 具体的には、has 演算子は contains 演算子よりも効率的です。 文字列処理の手法により、短い文字列よりも 4 文字を超える文字列を検索する方が効率的になります。

たとえば、次のクエリでは、Computer の名前付けポリシーに応じて、同様の結果が得られます。 しかし、2 番めの方が効率的です。

//less efficient – due to filter based on contains
Heartbeat
| where Computer contains "Production" 
| summarize count() by ComputerIP 
//less efficient – due to filter based on extend
Heartbeat
| extend MyComputer = Computer
| where MyComputer startswith "Production" 
| summarize count() by ComputerIP 
//more efficient
Heartbeat
| where Computer startswith "Production" 
| summarize count() by ComputerIP 

Note

この指標は、直近のクラスターの CPU のみを示します。 複数リージョンのクエリでは、リージョンのうち 1 つのみが表示されます。 複数ワークスペース クエリでは、一部のワークスペースが含まれない場合があります。

文字列解析が機能する場合は XML と JSON をフル解析しない

XML または JSON オブジェクトのフル解析は、CPU およびメモリ リソースを多く消費する場合があります。 多くの場合、必要なパラメーターが 1 つか 2 つだけで、XML または JSON オブジェクトが単純であるときは、文字列としてそれらを解析した方が簡単です。 parse 演算子またはその他のテキスト解析手法を使用してください。 XML または JSON オブジェクト内のレコード数が増えるため、パフォーマンスが大幅に向上します。 レコード数が千万単位に達するとき、これは非常に重要です。

たとえば次のクエリは、フル XML 解析を実行せずに上記のクエリと厳密に同じ結果を返します。 クエリにより、XML ファイルの構造にいくつかの想定 (FilePath 要素が FileHash の後にあってどちらの要素も属性を持たないなど) が行われます。

//even more efficient
SecurityEvent
| where EventID == 8002 //Only this event have FileHash
| where EventData !has "%SYSTEM32" //Early removal of unwanted records
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" *
| summarize count() by FileHash, FilePath
| where FileHash != "" // No need to filter out %SYSTEM32 here as it was removed before

処理されたクエリに使用するデータ

クエリ処理での重要な要因は、クエリ処理のためにスキャンして、使用されるデータの量です。 Azure Data Explorer では、他のデータ プラットフォームと比べ、データ量を大幅に削減する積極的な最適化が使用されています。 それでも、クエリには、使用されるデータ量に影響を与える可能性のある重要な要因があります。

2,000KB を超えるデータを処理するクエリは、リソースを過度に消費するクエリとみなされます。 20,000KB を超えるデータを処理するクエリはリソースを酷使するクエリと見なされ、スロットルされることがあります。

Azure Monitor ログでは、データにインデックスを付ける手段として、TimeGenerated 列が使用されます。 可能な限り範囲を狭くするように TimeGenerated 値を制限すると、クエリのパフォーマンスが向上します。 狭い範囲では、処理する必要があるデータの量が大幅に制限されます。

検索および和集合演算子を不必要に使わない

処理するデータが増加するもう 1 つの要因は、多数のテーブルの使用です。 このシナリオは通常、search * および union * コマンドの使用時に発生します。 これらのコマンドを実行すると、システムでは、強制的にワークスペース内のすべてのテーブルのデータが評価され、スキャンされます。 場合によっては、ワークスペース内には数百ものテーブルが存在することがあります。 search * や、範囲に特定のテーブルを指定しない検索の使用は、避けるようにしてください。

たとえば、次のクエリでは厳密に同じ結果が得られますが、最後のものが最も効率的です。

// This version scans all tables though only Perf has this kind of data
search "Processor Time" 
| summarize count(), avg(CounterValue)  by Computer
// This version scans all strings in Perf tables – much more efficient
Perf
| search "Processor Time" 
| summarize count(), avg(CounterValue)  by Computer
// This is the most efficient version 
Perf 
| where CounterName == "% Processor Time"  
| summarize count(), avg(CounterValue)  by Computer

初期フィルターをクエリに追加する

データ量を削減するもう 1 つの方法として、クエリの初期段階で where 条件を使用します。 Azure Data Explorer プラットフォームには、特定の where 条件に関連するデータを含むパーティションを認識できるようにするキャッシュが含まれています。 たとえば、クエリに where EventID == 4624 が含まれている場合、一致するイベントが含まれるパーティションを処理するノードにのみクエリが分散されます。

次のクエリ例では厳密に同じ結果が得られますが、2 番めの方が効率的です。

//less efficient
SecurityEvent
| summarize LoginSessions = dcount(LogonGuid) by Account
//more efficient
SecurityEvent
| where EventID == 4624 //Logon GUID is relevant only for logon event
| summarize LoginSessions = dcount(LogonGuid) by Account

条件付き集計関数および materialize 関数を使用して同じソース データの複数スキャンを回避する

join または union 演算子を使用してマージされた複数のサブクエリがクエリに含まれているときは、各サブクエリによってソース全体が別個にスキャンされます。 次に、結果がマージされます。 このアクションにより、データがスキャンされる回数が倍増します。これは、大きなデータセットでは重要な要素です。

このシナリオを回避する方法として、条件付き集計関数を使用します。 summary 演算子で使用される集計関数の大部分には、条件付きのバージョンがあります。これにより、複数の条件を持つ単一の summarize 演算子を使用できます。

たとえば、次のクエリは、ログイン イベントの数と各アカウントのプロセス実行イベントの数を示しています。 同じ結果が返されますが、最初のクエリではデータが 2 回スキャンされます。 2 番めのクエリでは、1 回だけスキャンされます。

//Scans the SecurityEvent table twice and perform expensive join
SecurityEvent
| where EventID == 4624 //Login event
| summarize LoginCount = count() by Account
| join 
(
    SecurityEvent
    | where EventID == 4688 //Process execution event
    | summarize ExecutionCount = count(), ExecutedProcesses = make_set(Process) by Account
) on Account
//Scan only once with no join
SecurityEvent
| where EventID == 4624 or EventID == 4688 //early filter
| summarize LoginCount = countif(EventID == 4624), ExecutionCount = countif(EventID == 4688), ExecutedProcesses = make_set_if(Process,EventID == 4688)  by Account

サブクエリが不要なもう 1 つのケースは、特定のパターンに一致するレコードのみを処理するように parse 演算子を事前フィルター処理することです。 これは、パターンが一致しない場合、parse 演算子およびその他の同様の演算子によって空の結果が返されるため、不要です。 次の 2 つのクエリでは厳密に同じ結果が返されますが、2 番めのクエリではデータが 1 回だけスキャンされます。 2 番目のクエリでは、各 parse コマンドはそのイベントのみに関連しています。 その後の extend 演算子は、空のデータ状況を参照する方法を示しています。

//Scan SecurityEvent table twice
union(
SecurityEvent
| where EventID == 8002 
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" *
| distinct FilePath
),(
SecurityEvent
| where EventID == 4799
| parse EventData with * "CallerProcessName\">" CallerProcessName1 "</Data>" * 
| distinct CallerProcessName1
)
//Single scan of the SecurityEvent table
SecurityEvent
| where EventID == 8002 or EventID == 4799
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" * //Relevant only for event 8002
| parse EventData with * "CallerProcessName\">" CallerProcessName1 "</Data>" *  //Relevant only for event 4799
| extend FilePath = iif(isempty(CallerProcessName1),FilePath,"")
| distinct FilePath, CallerProcessName1

このクエリでサブクエリの使用を回避できないときは、別の方法として materialize() 関数を使用して、それぞれに使用される単一のデータ ソースが存在するというヒントをクエリ エンジンに対して提供します。 この方法は、クエリ内で複数回使用される関数からソース データが取得されるときに便利です。 Materialize は、サブクエリの出力が入力よりもはるかに小さいときに効果的です。 クエリ エンジンによって、すべての発生において出力がキャッシュされ再利用されます。

取得する列の数を減らす

Azure Data Explorer は列形式のデータ ストアであるため、各列の取得は他の列とは関係がありません。 取得される列の数は、全体のデータ量に直接影響します。 結果を集計したり、特定の列を射影したりすることで、必要な列のみを出力に含める必要があります。

Azure Data Explorer には、取得される列の数を減らすために複数の最適化を行っています。 ある列が不要であると判断された場合 (たとえば、summarize コマンドで参照されていない場合)、それは取得されません。

たとえば、2 番めのクエリでは、1 つの列ではなく 3 つの列を取得する必要があるため、処理するデータが 3 倍になる可能性があります。

//Less columns --> Less data
SecurityEvent
| summarize count() by Computer  
//More columns --> More data
SecurityEvent
| summarize count(), dcount(EventID), avg(Level) by Computer  

処理されたクエリの期間

Azure Monitor ログ内のすべてのログは、TimeGenerated 列に従ってパーティション分割されます。 アクセスされるパーティションの数は、期間に直接関係します。 時間範囲を短縮することは、迅速なクエリ実行を確実にするための最も効率的な方法となります。

期間が 15 日間を超えるクエリは、リソースを過度に消費するクエリとみなされます。 期間が 90 日間を超えるクエリはリソースを酷使するクエリとみなされ、スロットルされることがあります。

時間範囲は、「Azure Monitor Log Analytics のログ クエリのスコープと時間範囲」で説明されているように、Log Analytics 画面で時間範囲セレクターを使用して設定できます。 選択した時間範囲がクエリ メタデータを使用してバックエンドに渡されるため、この方法をお勧めします。

別の方法として、クエリの TimeGeneratedwhere 条件を明示的に含めることもできます。 クエリが別のインターフェイスから使用される場合でも、期間が確実に固定されるため、この方法を使用してください。

クエリのすべての部分に TimeGenerated フィルターがあることを確認してください。 複数のテーブルまたは同じテーブルからデータを取得するサブクエリがクエリに含まれている場合、それぞれのクエリに独自の where 条件を含める必要があります。

すべてのサブクエリに TimeGenerated フィルターがあることを確認する

たとえば、次のクエリでは、Perf テーブルは過去 1 日分に対してのみスキャンされます。 Heartbeat テーブルは、その履歴をすべてスキャンします。これは、最大 2 年になる可能性があります。

Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
    Heartbeat
    //No time span filter in this part of the query
    | summarize IPs = makeset(ComputerIP, 10) by  Computer
) on Computer

このような誤りが発生する一般的なケースとして、直近の出現箇所の検索に arg_max () が使用されている場合が挙げられます。 次に例を示します。

Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
    Heartbeat
    //No time span filter in this part of the query
    | summarize arg_max(TimeGenerated, *), min(TimeGenerated)   
by Computer
) on Computer

この状況は、次のように、内部クエリに時間フィルターを追加することで簡単に修正できます。

Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
    Heartbeat
    | where TimeGenerated > ago(1d) //filter for this part
    | summarize arg_max(TimeGenerated, *), min(TimeGenerated)   
by Computer
) on Computer

この誤りのもう 1 つの例は、複数のテーブルに対する union の直後に時間範囲フィルターを実行するときに発生します。 union を実行するときは、各サブクエリの範囲を設定する必要があります。 let ステートメントを使用して、範囲の一貫性を確保できます。

たとえば、次のクエリは HeartbeatPerf テーブル内で、過去 1 日分だけでなくすべてのデータをスキャンします。

Heartbeat 
| summarize arg_min(TimeGenerated,*) by Computer
| union (
    Perf 
    | summarize arg_min(TimeGenerated,*) by Computer) 
| where TimeGenerated > ago(1d)
| summarize min(TimeGenerated) by Computer

クエリを修正すると次のようになります。

let MinTime = ago(1d);
Heartbeat 
| where TimeGenerated > MinTime
| summarize arg_min(TimeGenerated,*) by Computer
| union (
    Perf 
    | where TimeGenerated > MinTime
    | summarize arg_min(TimeGenerated,*) by Computer) 
| summarize min(TimeGenerated) by Computer

期間の測定値に関する制限

測定値は、指定された実際の時間よりも常に大きくなります。 たとえば、クエリのフィルターが 7 日の場合、システムでは 7.5 日分または 8.1 日分がスキャンされる可能性があります。 この変異は、システムがデータを可変サイズのチャンクにパーティション分割しているために発生します。 関連するすべてのレコードが確実にスキャンされるように、システムはパーティション全体をスキャンします。 このプロセスには、数時間、さらには 1 日以上かかる場合があります。

場合によっては、システムが正確な時間範囲の測定値を提供できないことがあります。 この状況はほとんどの場合、クエリの範囲が 1 日未満であるか、複数のワークスペース クエリに含まれている場合に発生します。

重要

この指標は、直近のクラスターで処理されたデータのみを示します。 複数リージョンのクエリでは、リージョンのうち 1 つのみが表示されます。 複数ワークスペース クエリでは、一部のワークスペースが含まれない場合があります。

処理されたデータの期間

Azure Data Explorer では、インメモリ、ローカルの SSD ディスク、および処理速度がはるかに遅い Azure BLOB という複数のストレージ層が使用されています。 データが新しいほど、待ち時間が短い、より高性能な層に格納される確率が高くなり、クエリ期間と CPU が低減されます。 システムには、データ自体に加え、メタデータ用のキャッシュもあります。 データが古くなるほど、メタデータがキャッシュ内に存在する確率が低くなります。

14 日以上が経過しているデータを処理するクリエは、リソースを過度に消費するクエリと見なされます。

一部のクエリでは古いデータの使用が必要ですが、古いデータが誤って使用されるケースもあります。 このシナリオは、メタデータに時間範囲を指定せずにクエリを実行したときに、一部のテーブル参照に TimeGenerated 列に対するフィルターが含まれていない場合に発生します。 このような場合、システムによって、そのテーブルに格納されているすべてのデータがスキャンされます。 データ保持期間が長いときは、長い時間範囲が対象になる可能性があります。 その結果、データ保持期間と同じだけ古いデータがスキャンされます。

たとえば、次のようなケースが該当する可能性があります。

  • Log Analytics で、制限されないサブクエリを使用して時間範囲を設定していない。 前の例を参照してください。
  • 時間範囲の省略可能なパラメーターを指定せずに API を使用している。
  • Power BI コネクタなどの時間範囲を強制しないクライアントを使用している。

前のセクションの例と注意事項が、このケースにも関連するため、参照してください。

リージョンの数

1 つのクエリがさまざまなリージョンにわたって実行される可能性がある状況があります。 次に例を示します。

  • 明示的に一覧表示されている複数のワークスペースが異なるリージョンに存在するとき。
  • リソースでスコープが指定されたクエリでデータを取得し、異なるリージョンに存在する複数のワークスペースにそのデータが格納される場合。

リージョンをまたがるクエリを実行するには、通常はクエリの最終結果よりはるかに大きい、バックエンドの中間データ チャンクをシステムでシリアル化して転送する必要があります。 最適化、ヒューリスティック、キャッシュの利用を行うためのシステムの機能も制限されます。

これらのリージョンすべてをスキャンする理由がない場合は、対象のリージョンが絞り込まれるようにスコープを調整してください。 リソースのスコープが最小化されても、多くのリージョンが使用されている場合は、構成の誤りが原因でそれが起きている可能性があります。 たとえば、監査ログと診断設定が異なるリージョンにある別々のワークスペースに送信されたり、診断設定の構成が複数存在したりする可能性があります。

リージョンが 3 つ以上にまたがるクエリは、リソースを過度に消費するクエリとみなされます。 リージョンが 6 つ以上にまたがるクエリはリソースを酷使するクエリとみなされ、スロットルされることがあります。

重要

1 つのクエリが複数のリージョンにわたって実行されると、CPU とデータの測定値が正確ではなくなり、いずれか 1 つのリージョンの測定値のみが表示されます。

ワークスペースの数

ワークスペースは、ログ データを分離して管理するために使用される論理コンテナーです。 バックエンドで、選択したリージョン内の物理クラスターでのワークスペースの配置が最適化されます。

複数のワークスペースを使用すると、次のときにインスタンスが発生する可能性があります。

  • 複数のワークスペースが明示的に一覧表示されている。
  • リソース範囲が指定されたクエリでデータを取得し、複数のワークスペースにそのデータが格納される。

リージョンおよびクラスターにまたがるクエリを実行するには、通常はクエリの最終結果よりはるかに大きい、バックエンドの中間データ チャンクをシステムでシリアル化して転送する必要があります。 最適化、ヒューリスティック、キャッシュの利用を行うためのシステムの機能も制限されます。

ワークスペースが 5 つ以上にまたがるクエリは、リソースを過度に消費するクエリとみなされます。 クエリが 100 を超えるワークスペースにまたがることはありません。

重要

  • 複数ワークスペースのシナリオでは、CPU とデータの測定値が正確ではなくなり、ごく一部のワークスペースに測定値のみが表示されます。
  • 明示的な識別子 (ワークスペース ID、またはワークスペースの Azure リソース ID) を持つクロス ワークスペース クエリは、消費するリソースが少なくなり、パフォーマンスが向上します。

Parallelism

Azure Monitor ログでは、Azure Data Explorer の大規模なクラスターを使用してクエリを実行します。 これらのクラスターはスケールがさまざまで、最大数十の計算ノードを含む可能性があります。 このシステムでは、ワークスペースの配置ロジックと容量に応じて、クラスターが自動的にスケーリングされます。

クエリを効率的に実行するために、クエリは、その処理に必要なデータに基づいてパーティション分割され、計算ノードに分散されます。 状況によっては、システムでこの手順を効率的に実行できないため、クエリの実行時間が長くなる可能性があります。

並列処理を低減できるクエリ動作は次のとおりです。

  • シリアル化関数やウィンドウ関数 (serialize 演算子next ()prev ()row 関数など) の使用。 このようなケースのいくつかでは、時系列およびユーザー分析関数を使用できます。 rangesortordertoptop-hittersgetschema 演算子がクエリの末尾以外で使用されている場合にも、非効率的なシリアル化が発生する可能性があります。
  • dcount () 集計関数を使用すると、システムで個別の値の中央コピーが強制的に保持されます。 データのスケールが大きいときは、dcount 関数の省略可能なパラメーターを使用して精度を下げることを検討してください。
  • 多くの場合、join 演算子を使用すると全体的な並列処理が低下します。 パフォーマンスに問題があるときは、代替手段として shuffle join を検討してください。
  • リソースのスコープが指定されたクエリで、非常に多くの Azure のロールの割り当てがある状況では、実行前の Kubernetes ロールベースのアクセス制御 (RBAC) または Azure RBAC チェックに時間がかかることがあります。 この状況では、チェックにかかる時間が長くなり、並列処理が低下する可能性があります。 たとえば、多数のリソースがあり、各リソースに、サブスクリプションやリソース グループではなくリソース レベルでロールの割り当てが多数存在するサブスクリプションに対してクエリが実行されます。
  • クエリで小さなデータ チャンクを処理している場合、システムがクエリを多数の計算ノードに分散させないため、その並列処理は少なくなります。

次のステップ

Kusto 照会言語のリファレンス ドキュメント