Оптимизация запросов журнала в мониторе Azure

Azure Monitor Logs — это полностью управляемая облачная служба, предназначенная для автоматической обработки сбора данных, индексирования и выполнения запросов в условиях больших и изменяющихся нагрузок. Его базовый механизм использует встроенные механизмы, которые оптимизируют выполнение запросов, распределяют обработку и автоматически масштабировать ресурсы без вмешательства пользователя.

журналы мониторинга Azure используют Azure Data Explorer в составе базового ядра для хранения данных журнала и выполнения запросов для анализа этих данных. Он создает, управляет и поддерживает кластеры Azure Data Explorer для вас и оптимизирует их для рабочих нагрузок анализа журналов. При выполнении запроса служба оптимизирует его и направляет его в соответствующий кластер Azure Data Explorer, в который хранятся данные рабочей области.

Azure Monitor Logs и Azure Data Explorer используют множество механизмов автоматической оптимизации запросов. Как и в любой крупной аналитической системе, выполнение запросов в очень больших наборах данных требует дополнительных вычислительных ресурсов и может повлиять на производительность запросов. Хотя автоматические оптимизации обеспечивают значительное повышение производительности запросов, в некоторых случаях можно значительно повысить производительность запросов. В этой статье рассматриваются вопросы производительности и несколько способов их решения.

Большинство методов часто используются для запросов, выполняемых непосредственно в Azure Data Explorer и Azure журналах мониторинга. Также рассматриваются несколько уникальных рекомендаций по мониторингу журналов Azure. Дополнительные советы по оптимизации Azure Data Explorer см. в разделе Рекомендации.

Оптимизированные запросы:

  • Запустите быстрее и уменьшите общую длительность выполнения запроса.
  • Меньше вероятность, что они будут подвергнуты ограничениям или отклонены.

Обратите особое внимание на запросы, которые используются для периодического и одновременного использования, таких как панели мониторинга, оповещения, Azure Logic Apps и Power BI. В таких случаях влияние неэффективного запроса является существенным. В этих сценариях рассмотрите правила создания сводных таблиц для создания и управления сводными таблицами в рабочей области Log Analytics. Сводные правила повторно собирают данные больших наборов данных после определенной задержки, создавая сводные таблицы. Сводные таблицы запрашиваются более эффективно, чем исходные необработанные данные.

Ниже приведено подробное видеоруководство по оптимизации запросов.

Панель "Сведения о запросе"

После запуска запроса в Log Analytics выберите сведения о запросе в правом нижнем углу экрана, чтобы открыть панель сведений о запросе. Область сведений о запросе включает три вкладки:

  • Обзор . Курированный набор ключевых показателей производительности
  • Необработанные статистические данные . Все подробные данные о выполнении, ссылки на рабочие области и запросы и другие технические данные
  • Ошибки . Выводит список ошибок, возникших во время выполнения запроса

На этой вкладке "Обзор" показаны результаты нескольких индикаторов производительности для запроса. Эти показатели производительности описаны в следующем разделе.

Скриншот, показывающий панель сведений о запросе в Azure Monitor Log Analytics.

Разбивка времени выполнения запроса

При анализе производительности запросов в журналах мониторинга Azure область сведений о запросе предоставляет время Execution, которое отражает общую длительность запроса от конца к концу. Эта статистика разбита на три отдельных компонента, которые помогут определить узкие места производительности:

  1. Engine Execution Time Это время, затраченное на выполнение запроса в базовом обработчике данных, например Azure Data Explorer или других компонентов. Если это значение является основным участником общего времени выполнения, это может указывать на то, что сам запрос можно оптимизировать. Ознакомьтесь с методами оптимизации, описанными в этом документе, чтобы повысить производительность.

  2. Время выполнения операций службы Это время, затраченное в самой службе журналов Azure Monitor, за пределами подсистемы данных. Она включает внутреннюю обработку и оркестрацию.

  3. Время ожидания в очереди службы Это время, которое запрос провел в ожидании в очереди службы журналов Azure Monitor перед выполнением. Если это значение заполнено (например, не значение по умолчанию N/A), то предполагается, что пользователь достиг ограничений параллелизма из-за нескольких одновременных запросов. Большое время очереди указывает, что другие одновременные запросы могут быть ресурсоемкими и должны быть проверены и оптимизированы для уменьшения состязаний. Ознакомьтесь с ограничениями службы Azure Monitor - Azure Monitor | Microsoft Learn. Azure Мониторинг ограничений службы — регулирование запросов пользователей

Показатели производительности запросов

Каждый выполняемый запрос предоставляет следующие показатели производительности запросов:

  • Всего ЦП. Общий объем вычислений при обработке запроса на всех вычислительных узлах. Представляет значение времени, затраченное на вычисления, анализ и получение данных.
  • Период времени обработанного запроса: разрыв между новейшими и самыми старыми данными, к которым обращается запрос. Явный диапазон времени, указанный для запроса, влияет на этот индикатор.
  • Возраст обработанных данных: разрыв между сейчас и самыми старыми данными, к которым обращается запрос. Этот индикатор сильно влияет на эффективность получения данных.
  • Количество рабочих областей: сколько рабочих областей, к которых обращается запрос во время обработки запросов на основе неявного или явного выбора.
  • Количество регионов: сколько регионов, к которых обращается запрос во время обработки запросов на основе неявного или явного выбора рабочих областей. Запросы по нескольким регионам гораздо менее эффективны, а показатели производительности дают лишь частичное представление.
  • Параллелизм: сколько система выполнила запрос на нескольких узлах. Этот индикатор относится только к запросам с высоким потреблением ЦП. Использование определенных функций и операторов влияет на этот индикатор.
  • Пик памяти: максимальный объем памяти, используемой системой при выполнении этого запроса. Это значение включает память, потребляемую загрузкой данных, обработкой и временным хранением во время выполнения.

Всего ЦП

Эта метрика показывает фактический вычислительный ЦП, обрабатывающий запрос на всех узлах обработки запросов. Так как большинство запросов выполняются на большом количестве узлов, общая сумма обычно гораздо больше длительности выполнения запроса.

Если запрос использует более 100 секунд ЦП, он потребляет чрезмерные ресурсы. Если запрос использует более 1000 секунд ЦП, он считается оскорбительным запросом и может регулироваться.

Время обработки запроса тратится на:

  • Получение данных. Получение старых данных занимает больше времени, чем получение последних данных.
  • Обработка данных: логика и оценка данных.

Помимо времени, затраченного на узлы обработки запросов, в Azure Monitor Logs также требуется время на:

  • Проверка подлинности пользователя и проверка наличия у них разрешения на доступ к этим данным.
  • Поиск хранилища данных.
  • Анализ запроса.
  • Выделение узлов обработки запросов.

Время, затраченное на это, не включается в общее процессорное время запроса.

Ранняя фильтрация записей перед использованием ресурсоемких функций ЦП

Некоторые команды запросов и функции используют высокий уровень ЦП. Это особенно верно для команд, которые анализируют JSON и XML или извлекают сложные регулярные выражения. Такой анализ может происходить явным образом с помощью функций parse_json() или parse_xml() или неявно, когда он ссылается на динамические столбцы.

Эти функции используют ЦП пропорционально количеству обрабатываемых строк. Наиболее эффективной оптимизацией является добавление where условий в начале запроса. Добавив эти условия, вы можете отфильтровать как можно больше записей перед запуском функции с интенсивным ЦП.

Например, следующие запросы создают точно тот же результат. Но второй является наиболее эффективным, так как условие 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 для вычисляемого столбца, а не столбцов, которые физически присутствуют в наборе данных, теряют эффективность. Фильтрация по вычисляемым столбцам предотвращает некоторые оптимизации системы при обработке больших наборов данных.

Например, следующие запросы создают точно тот же результат. Но второй является более эффективным, так как условие 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() имеют низкое влияние на ЦП из-за их логики. Другие команды являются более сложными и включают эвристики и оценки, которые позволяют эффективно выполнять их. Например, dcount() использует алгоритм HyperLogLog для обеспечения близкой оценки к определенному количеству больших наборов данных без фактического подсчета каждого значения.

Функции процентиля выполняют аналогичные приближения, используя алгоритм процентиля ближайшего ранга. Некоторые команды включают необязательные параметры, которые снижают их влияние. Например, функция makeset() имеет необязательный параметр для определения максимального размера набора, который значительно влияет на ЦП и память.

команды Join и summarize могут привести к высокой загрузке ЦП при обработке большого набора данных. Их сложность напрямую связана с количеством возможных значений, называемых кардинальность, столбцов, используемых либо в качестве by в summarize, либо в качестве join атрибутов. Для объяснения и оптимизации join и summarize см. их статьи документации и советы по оптимизации. Например, следующие запросы создают точно тот же результат, так как CounterPath всегда сопоставляется один к одному CounterName и ObjectName. Второй запрос эффективнее, так как измерение агрегирования меньше:

//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

Потребление ЦП также может быть затронуто where условиями или расширенными столбцами, которые требуют интенсивных вычислений. Все тривиальные сравнения строк, такие как equal == и startswith, имеют примерно то же влияние ЦП. Продвинутые текстовые совпадения оказывают больше влияния. В частности, оператор has эффективнее, чем оператор contains. Из-за методов обработки строк более эффективно искать строки, которые длиннее четырех символов, чем короткие строки.

Например, следующие запросы создают аналогичные результаты в зависимости от Computer политики именования. Но второй является более эффективным:

//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 

Примечание.

Этот показатель представляет только ЦП из ближайшего кластера. В многорегионном запросе он представляет только один из регионов. В запросе с несколькими рабочими областями могут быть не включены все рабочие области.

Избегайте полного анализа XML и JSON, если достаточно анализа строк.

Полный анализ объекта XML или JSON может использовать большие ресурсы ЦП и памяти. Во многих случаях, если требуется только один или два параметра, а объекты 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

Разделите большие команды синтаксического анализа

При использовании оператора parse ограничьте количество извлекаемых столбцов до пяти в одной инструкции. Чрезмерное количество извлечений в одной инструкции может привести к значительному увеличению времени обработки. Вместо этого разделите извлечения на несколько parse операторов.

Примечание.

В преобразованиях parse оператор ограничен 10 извлечениями в одном выражении.

Например, следующие запросы создают одинаковые результаты, но второй значительно эффективнее, так как он разбивает операцию синтаксического анализа на несколько небольших команд.

//less efficient
LogData
| parse Message with
   * "field1=" Field1: string
   " field2=" Field2: string
   " field3=" Field3: string
   " field4=" Field4: string
   " field5=" Field5: string
   " field6=" Field6: string
   " field7=" Field7: string
   " field8=" Field8: string
   " field9=" Field9: string
   " field10=" Field10: string *
//more efficient
LogData
| parse Message with
   * "field1=" Field1: string
   " field2=" Field2: string
   " field3=" Field3: string
   " field4=" Field4: string
   " field5=" Field5: string *
| parse Message with
   * " field6=" Field6: string
   " field7=" Field7: string
   " field8=" Field8: string
   " field9=" Field9: string
   " field10=" Field10: string *

Предотвращение ненужного использования операторов search и union

Фактор, повышающий обработку данных, — использование большого количества таблиц. Этот сценарий обычно происходит при использовании 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

Добавление фильтров в начале запроса

Вы можете уменьшить объем данных, добавив условия where в начале запроса. Платформа Azure Data Explorer включает кэш, который позволяет ему знать, какие секции включают данные, соответствующие определенному условию where. Например, если запрос содержит where EventID == 4624, он распределяет запрос только на узлы, обрабатывающие секции с соответствующими событиями.

В следующем примере запросы создают точно тот же результат, но второй является более эффективным:

//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

Избегайте нескольких проверок одинаковых исходных данных

Если запрос содержит несколько вложенных запросов, которые объединяются операторами соединения или объединения, каждый вложенный запрос сканирует весь источник отдельно. Затем запрос объединяет результаты. Это действие умножает количество сканируемых данных, что является критически важным фактором в производительности запросов большого набора данных.

Чтобы избежать этой проблемы с производительностью, используйте функции условной статистической обработки. Большинство функций aggregation, используемых в операторе сводки, имеют условную версию. Используйте условную версию, чтобы получить один оператор сводки с несколькими условиями.

Например, в следующих запросах отображается количество событий входа и количество событий выполнения процесса для каждой учетной записи. Они возвращают одинаковые результаты, но первый запрос проверяет данные дважды. Второй запрос сканирует его только один раз:

//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

Другой случай, когда вложенные запросы являются ненужными, является префильтровка для оператора parse, чтобы убедиться, что она обрабатывает только записи, соответствующие определенному шаблону. Они не нужны, так как оператор синтаксического анализа и другие аналогичные операторы возвращают пустые результаты, если шаблон не соответствует. Следующие два запроса возвращают точно те же результаты, но второй запрос сканирует данные только один раз. Во втором запросе каждая команда 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, он не извлекает его.

Например, второй запрос может обрабатывать три раза больше данных, так как он должен получить не один столбец, но три:

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

Интервал времени обработанного запроса

Все журналы в журналах мониторинга Azure секционируются в соответствии со столбцом TimeGenerated. Количество секций, к которым обращается запрос, напрямую связано с интервалом времени. Сокращение диапазона времени является наиболее эффективным способом обеспечить выполнение запроса.

Запрос с интервалом времени более 15 дней считается запросом, который потребляет чрезмерные ресурсы. Запрос с временным интервалом более 90 дней считается нагрузочным запросом и может ограничиваться.

Диапазон времени можно задать с помощью селектора диапазона времени на экране Log Analytics, как описано в разделе Log query scope and time range in Azure Monitor Log Analytics. Этот метод рекомендуется, так как выбранный диапазон времени передается на серверную часть через метаданные запроса.

Альтернативный метод заключается в явном включении условия where для TimeGenerated в запросе. Используйте этот метод, так как он гарантирует исправление интервала времени, даже если запрос используется из другого интерфейса.

Убедитесь, что все части запроса имеют TimeGenerated фильтры. Если запрос содержит вложенные запросы для получения данных из разных таблиц или одной таблицы, каждый запрос должен содержать собственное условие where.

Убедитесь, что все вложенные запросы имеют фильтр TimeGenerated

Например, в следующем запросе Perf таблица сканируется только за последний день. Таблица Heartbeat сканируется на всю историю, которая может составлять до двух лет:

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

Еще одним примером этой ошибки является фильтрация по временной шкале сразу после union по нескольким таблицам. При выполнении объединения каждый вложенный запрос должен быть ограничен в контексте. Можно использовать оператор [let]'(/azure/kusto/query/letstatement), чтобы обеспечить согласованность области.

Например, следующий запрос сканирует все данные в Heartbeat таблицах и Perf не только последний день:

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,5 или 8,1 дня. Это отклонение обусловлено тем, что система секционирует данные на блоки переменных размеров. Чтобы убедиться, что запрос сканирует все соответствующие записи, система сканирует всю секцию. Этот процесс может охватывать несколько часов и даже более одного дня.

В нескольких случаях система не обеспечивает точное измерение диапазона времени. Эта ситуация происходит в большинстве случаев, когда диапазон запроса меньше дня или в запросах с несколькими рабочими областями.

Внимание

Этот показатель отображает только данные, обработанные в ближайшем кластере. В многорегионном запросе он представляет только один из регионов. В запросе с несколькими рабочими областями могут быть не включены все рабочие области.

Возраст обработанных данных

Azure Data Explorer использует несколько уровней хранения: в памяти, на локальных дисках SSD и гораздо более медленные объекты Azure Blob. Чем более новые данные, тем выше вероятность того, что она хранится на более производительном уровне с меньшей задержкой, что снижает длительность запроса и ЦП. Кроме самих данных, система также имеет кэш для метаданных. Чем старше данные, тем меньше шансов, что его метаданные хранятся в кэше.

Запрос, обрабатывающий данные более 14 дней, считается запросом, который потребляет чрезмерные ресурсы.

Для некоторых запросов требуется использование старых данных, но некоторые запросы используют старые данные по ошибке. Этот сценарий происходит, когда запросы выполняются без предоставления диапазона времени в метаданных, а не все ссылки на таблицы включают фильтр в столбце TimeGenerated . В этих случаях система сканирует все данные, хранящиеся в таблице. Если срок хранения данных длительный, он может охватывать длительные диапазоны времени. В результате сканируются данные, возраст которых соответствует периоду хранения данных.

Примеры таких случаев включают следующие ситуации:

  • Отсутствие настройки диапазона времени в Log Analytics в подзапросе, который не ограничен. См. предыдущий пример.
  • Использование API без необязательных параметров диапазона времени.
  • Использование клиента, который не принудительно задает диапазон времени, например соединитель Power BI.

Ознакомьтесь с примерами и заметками в предыдущем разделе, так как они также относятся к этому делу.

Количество регионов

Ситуации существуют, когда один запрос может выполняться в разных регионах. Например:

  • Вы явно перечисляете несколько рабочих областей, и они находятся в разных регионах.
  • Запрос, ограниченный областью ресурсов, извлекает данные, которые хранятся в нескольких рабочих пространствах, расположенных в разных регионах.

Выполнение запросов между регионами требует от системы сериализации и передачи в фоновом режиме больших объемов промежуточных данных, которые обычно значительно больше, чем окончательные результаты запроса. Она также ограничивает способность системы выполнять оптимизации и эвристики и использовать кэши.

Если вам не нужно сканировать все эти регионы, настройте область, чтобы она охватывала меньше регионов. Если свести к минимуму область ресурсов, но многие регионы по-прежнему используются, возможной причиной может быть неправильная настройка. Например, журналы аудита и параметры диагностики могут быть отправлены в разные рабочие области в разных регионах или могут быть несколько конфигураций параметров диагностики.

Запрос, охватывающий более трех регионов, считается запросом, который потребляет избыточные ресурсы. Запрос, охватывающий более шести регионов, считается неправомерным запросом и может ограничиваться.

Внимание

При выполнении запроса в нескольких регионах измерения ЦП и данных не являются точными и представляют измерение только одного из регионов.

Число рабочих пространств

Используйте рабочие области в качестве логических контейнеров для разделения и администрирования данных журналов. Серверная часть оптимизирует размещение рабочих областей в физических кластерах в выбранном регионе.

В результате этих ситуаций создаются несколько рабочих областей:

  • Несколько рабочих областей явно перечислены.
  • Запрос, ориентированный на ресурсы, извлекает данные, хранящиеся в нескольких рабочих областях.

Выполнение запросов между регионами и несколькими кластерами требует от системы сериализации и передачи в внутренних больших блоках промежуточных данных, которые обычно гораздо больше, чем конечные результаты запроса. Она также ограничивает способность системы выполнять оптимизации и эвристики и использовать кэши.

Запрос, охватывающий более пяти рабочих областей, потребляет избыточные ресурсы. Запросы не могут охватывать более 100 рабочих областей.

Внимание

  • В некоторых сценариях с несколькими рабочими областями измерения ЦП и данных не являются точными и представляют измерение только нескольких рабочих областей.
  • Запросы между рабочими областями с явным идентификатором, например идентификатором рабочей области или Azure Resource ID, потребляют меньше ресурсов и работают (операции выполняются) лучше.

Дополнительные сведения см. в разделе "Запрос между ресурсами".

Параллелизм

Azure Monitor Logs используют большие кластеры Azure Data Explorer для выполнения запросов. Эти кластеры зависят от масштаба и могут включать до десятков вычислительных узлов. Система автоматически масштабирует кластеры в соответствии с логикой и емкостью размещения рабочих областей.

Чтобы эффективно выполнить запрос, система секционирует запрос и распределяет его на вычислительные узлы на основе данных, необходимых для обработки. В некоторых ситуациях система не может эффективно выполнять этот шаг, что может привести к длительной продолжительности запроса.

Поведения запросов, которые могут сократить параллелизм:

  • Во многих случаях такие операторы, как join и summarize, снижают общий уровень параллелизма. Рекомендуется использовать shuffle, если производительность проблематична. Используйте запрос перетасовки, если ключ (другими словами, присоединяемый или агрегируемый столбец) имеет множество уникальных значений (высокая кардинальность). Например, используйте перемешивание, если столбец содержит общедоступные IP-адреса. Избегайте использования перемешивания для ключей с низкой кардинальностью (например, уровень серьезности события). Явно используйте hint.shufflekey, когда работаете с несколькими запросами перетасовки, чтобы гарантировать, что каждый ключ вступает в силу.
  • Использование функций сериализации и окон, таких как оператор serialize, next(), prev() и функции row. В некоторых случаях можно использовать временные ряды и функции аналитики пользователей. Неэффективная сериализация также может произойти, если следующие операторы не используются в конце запроса: range, sort, order, top, top-hitters и getschema.
  • Использование функции агрегирования dcount() заставляет систему иметь центральную копию отдельных значений. Если масштаб данных велик, рассмотрите возможность использования необязательных параметров функции dcount для снижения точности.
  • В запросах с областью ресурсов предварительное выполнение проверки ролевого управления доступом (RBAC) в Kubernetes или проверки Azure RBAC могут задерживаться при большом количестве назначения ролей Azure. Эта ситуация может привести к более длительным проверкам, что приводит к снижению параллелизма. Например, запрос может выполняться в подписке, где существуют тысячи ресурсов, и каждый ресурс имеет множество назначений ролей на уровне ресурса, а не в подписке или группе ресурсов.
  • Если запрос обрабатывает небольшие фрагменты данных, его параллелизм низк, так как система не распределяет ее по многим вычислительным узлам.

Пиковое значения использования памяти

Пик памяти — это максимальный объем ОЗУ, наблюдаемый подсистемой Azure Data Explorer при выполнении запроса. Он охватывает память, используемую для загрузки данных (кэш/горячие операции чтения), обработку операторов (например, присоединение, сводку, создание рядов) и временные рабочие наборы. Это ведущий индикатор неконтролируемого увеличения памяти, который активирует защитные механизмы, такие как перегрузка запросов (E_RUNAWAY_QUERY, оператор превысил бюджет памяти) и E_LOW_MEMORY_CONDITION. Мониторинг пиков памяти помогает обнаруживать эти шаблоны на ранних стадиях и настраивать запросы до того, как они достигнут связанных жёстких ограничений.

Уменьшение пиковой нагрузки памяти

  • Следуйте тем же рекомендациям, что и для всего ЦП. В частности, для каждой таблицы в запросе применяется ранняя фильтрация записей и проекция столбцов.
  • Во многих случаях такие операторы, как join и summarize, приводят к высокой нагрузке на память и могут привести к неконтролируемому выполнению запроса. Рекомендуется использовать shuffle, если производительность проблематична. Используйте перетасовку, если ключ (то есть столбец, который объединяется или обобщается) имеет высокую кардинальность. Например, используйте перемешивание, если столбец содержит общедоступные IP-адреса. Избегайте использования перемешивания для ключей с низкой кардинальностью (например, уровень серьезности события).
  • Если вы используете join, используйте рекомендации, где это применимо. См. рекомендации Query.
  • Рекомендуется использовать выборку.

Справочная документация языка запросов Kusto