Azure 監視器記錄會使用 Azure 資料總管來儲存記錄資料,並執行查詢來分析該資料。 這會為您建立、管理和維護 Azure Data Explorer 叢集,並針對記錄分析工作負載將其最佳化。 當您執行查詢時,會對查詢進行最佳化,並將其路由傳送至可儲存工作區資料的適當 Azure Data Explorer 叢集。
Azure 監視器記錄和 Azure Data Explorer 會使用許多自動查詢最佳化機制。 自動最佳化可提供大幅提升,但在某些情況下也可以大幅改善查詢效能。 本文說明效能考量和數種修正技術。
大部分的技術都適用於直接在 Azure Data Explorer 和 Azure 監視器記錄上執行的查詢。 我們也將會探討數個獨特的 Azure 監視器記錄考量。 如需更多 Azure Data Explorer 最佳化秘訣,請參閱查詢最佳做法。
最佳化查詢將會:
- 執行更快速,並減少執行查詢的整體持續時間。
- 有少許的機會可以進行節流或拒絕。
請特別注意用於週期性和模擬使用量的查詢,例如儀表板、警示、Azure Logic Apps 和 Power BI。 在這些情況下,無效查詢的影響很大。
以下是關於查詢最佳化的詳細影片逐步解說。
查詢詳細資料窗格
在 Log Analytics 中執行查詢之後,請選取畫面右下角的 [查詢詳細資料],以開啟 [查詢詳細資料] 窗格。 [查詢詳細資料] 窗格包含三個索引標籤:
- 概觀 - 關鍵效能指標的策展集
- 原始統計資料 - 所有詳細的執行統計資料、參照的工作區與查詢,以及其他技術資料
- 錯誤 - 列出查詢執行期間所遇到的任何錯誤
此概觀索引標籤顯示該查詢的幾個效能指標結果。 下節將說明這些效能指標。
查詢執行時間明細
在分析 Azure 監視器記錄中的查詢效能時,[查詢詳細資料] 窗格會提供 [執行時間],其中反映該查詢從開始至結束的整體持續時間。 此統計資料分為三個相異的元件,以協助識別效能瓶頸:
引擎執行時間:這是查詢在基礎資料引擎 (例如 Azure 資料總管或其他元件) 中執行所花費的時間。 如果此值為整體執行時間的主要參與者,可能表示查詢本身還可以最佳化。 請參閱本文件所述的最佳化技術以提升效能。
服務執行時間:這表示在 Azure 監視器記錄服務本身內 (不含資料引擎) 所花費的時間。 它包含內部處理與協調流程。
服務佇列時間:這表示查詢在執行前,於 Azure 監視器記錄服務佇列中等待所花費的時間。 如果已填入此值 (例如不是預設值 N/A),則是建議使用者已因多筆同時執行的查詢達到並行限制。 高佇列時間表示其他同時執行的查詢可能資源密集,應進行檢閱與最佳化以減少爭用。 請參閱 Azure 監視器服務限制 - Azure 監視器 | Microsoft Learn。 Azure 監視器服務限制 - 使用者查詢節流
查詢效能指標
下列查詢效能指標適用於每個執行的查詢:
- CPU 總計:用來處理跨所有計算節點的查詢的整體計算。 其代表用於計算、剖析和資料擷取的時間。
- 已處理查詢的時間範圍:已存取以處理查詢的最新資料與最舊資料之間的差距。 受到指定給查詢的明確時間範圍的影響。
- 已處理資料的存留期:已存取以處理查詢的現在資料與最舊資料之間的差距。 其高度影響資料擷取的效率。
- 工作區數目:根據隱含或明確選取在查詢處理期間存取的工作區數目。
- 區域數目:根據隱含或明確選取的工作區,在查詢處理期間存取多少區域。 多區域查詢的效率較低,而效能指標會呈現部分涵蓋範圍。
- 平行處理原則:指出系統能夠在多個節點上執行此查詢的程度。 僅與具有高 CPU 耗用量的查詢相關。 受到使用特定函數和運算子的影響。
- 記憶體峰值:系統在執行此查詢時所使用的最大記憶體量。 這包括在執行期間因資料載入、處理和暫時儲存所耗用的記憶體。
CPU 總數
投資在所有查詢處理節點間以處理此查詢的實際計算 CPU。 由於大部分查詢都是在大量節點上執行,因此這通常會大幅高於執行查詢所花費的持續時間。
使用超過 100 秒 CPU 的查詢會被視為耗用過多資源的查詢。 使用超過 1,000 秒 CPU 的查詢會被視為濫用查詢,而且可能會對其進行節流。
查詢處理所需的時間主要用於以下幾個方面:
- 資料擷取:擷取舊資料所耗用的時間將會多於擷取最近資料。
- 資料處理:資料的邏輯和評估。
除了查詢處理節點所花費的時間之外,Azure 監視器記錄也會在這些項目花費時間:
- 驗證使用者並確認他們有權存取此資料。
- 尋找資料存放區。
- 剖析查詢。
- 配置查詢處理節點。
查詢總 CPU 時間不包括此時間。
在使用高 CPU 函式之前,先初步篩選記錄
有些查詢命令和函數需要耗用極大的 CPU。 尤其適用於剖析 JSON 和 XML 或擷取複雜的正則表達式的命令。 這類剖析可以透過 parse_json() 或 parse_xml() 函式明確地進行,或在其參照動態資料行時隱含地進行。
這些函式耗用的 CPU 與處理的資料列數目成比例。 最有效率的最佳化是在查詢初期新增 where 條件。 如此一來,就可以在執行 CPU 密集函式之前儘量篩選出記錄。
例如,下列查詢將產生完全相同的結果。 不過,第二個查詢最有效率,因為剖析前的 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() 由於其背後運作邏輯,對 CPU 影響較低。 其他命令比較複雜並包含啟發學習法和預估,以便有效率地執行。 例如,dcount() 使用 HyperLogLog 演算法,在不實際計算每個值的情況下提供大型資料集相異計數的貼近真實情況的預估。
百分位數函式會使用最接近的排名百分位數演算法,進行類似的近似。 數個命令包括可減少其影響的選用參數。 例如,makeset() 函數具有選用參數可定義集合大小上限,而這會大幅影響 CPU 和記憶體。
join 和 summarize 命令在處理大量資料時可能會造成高 CPU 使用率。 其複雜度與資料行的可能值數目 (稱為「基數」) 直接相關,而資料行在 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
CPU 耗用量也可能受到需要大量計算的 where 條件或擴充資料行的影響。 所有簡單的字串比較 (例如 equal == 和 startswith) 對 CPU 的影響大致相同。 進階文字比對的影響較大。 具體而言,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
附註
此指標只會呈現來自立即叢集的 CPU。 在多區域查詢中,這只代表其中一個區域。 在多工作區查詢中,這可能未包括所有工作區。
避免在字串剖析運作時進行完整 XML 和 JSON 剖析
XML 或 JSON 物件的完整剖析可能會耗用高 CPU 和記憶體資源。 在許多情況下,如果只需要一兩個參數且 XML 或 JSON 物件很簡單,則將其剖析為字串會更輕鬆。 使用剖析運算子或其他文字剖析技術。 效能提升將會隨著 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
避免對相同的來源資料進行多次掃描
當查詢有幾個子查詢透過 join 或 union 運算子合併時,每個子查詢都會個別掃描整個來源。 然後其會合併結果。 此動作會乘以掃描資料的次數,這是大型資料集查詢效能的關鍵因素。
避免此效能問題的技術是使用條件式彙總函式。 摘要運算子中使用的大部分 彙總函數 都有條件式版本。 使用條件式版本來取得具有多個條件的單一摘要運算子。
例如,下列查詢會顯示每個帳戶的登入事件數目和程序執行事件數目。 其會傳回相同的結果,但第一個查詢會掃描資料兩次。 第二個查詢只會掃描一次:
//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 畫面中的時間範圍選取器來設定時間範圍,如 Azure 監視器 Log Analytics 中的記錄查詢範圍和時間範圍中所述。 建議使用這種方法,因為選取的時間範圍會使用查詢中繼資料傳送至後端。
替代方法是在查詢的 上明確包括 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 陳述式來確保一致性範圍。
例如,下列查詢將會掃描 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 天,則系統可能會掃描 7.5 或 8.1 天。 此變異數是因為系統將資料分割成可變大小的區塊。 為了確保掃描所有相關記錄,系統會掃描整個分割區。 此流程可能會涵蓋數小時甚至超過一天。
在一些情況下,系統無法準確測量時間範圍。 在查詢範圍少於一天或在多工作區查詢的大部分情況下,就會發生這種情況。
重要
此指標只會顯示立即叢集中所處理的資料。 在多區域查詢中,這只代表其中一個區域。 在多工作區查詢中,這可能未包括所有工作區。
已處理資料的存留期
Azure Data Explorer 使用數個儲存層級:記憶體儲存、本機 SSD 磁碟,以及速度較慢的 Azure Blob 儲存體。 資料越新,就越有可能儲存在較高效能且延遲較短的層,從而降低查詢持續時間和 CPU。 除了資料本身之外,系統也有中繼資料快取。 資料越舊,其中繼資料在快取中的機會就越少。
如果查詢所處理的資料超過 14 天,則視為耗用過多資源。
雖然有些查詢需要使用舊資料,但某些情況下,則是誤用到舊資料。 如果執行查詢但未在其中繼資料內提供時間範圍,而且並非所有資料表參考都包括對 TimeGenerated 資料行的篩選,則會發生這種情況。 在這些情況下,系統將會掃描該資料表中所儲存的所有資料。 當資料保留期很長時,其可以涵蓋很長的時間範圍。 因此,系統會掃描與資料保留期間一樣舊的資料。
例如,這類情況可以是:
- 對於沒有受限的子查詢,未在 Log Analytics 中設定時間範圍。 請查看上述範例。
- 在不使用時間範圍選填參數的情況下使用 API。
- 使用不會強制時間範圍的客戶端,如 Power BI 連接器。
請參閱上一節的範例和注意事項,因為這些也與此案例有關。
區域數目
在一些情況下,可能會跨不同的區域執行單一查詢。 例如:
- 當幾個工作區被明確列出且位於不同區域時。
- 當資源範圍查詢正在擷取資料,而且資料儲存於位於不同區域的多個工作區時。
跨區域查詢執行需要系統在後端大型中繼資料區塊中序列化和傳輸,而這些區塊通常會略大於查詢最終結果。 這也會限制系統執行最佳化、啟發學習法和利用快取的能力。
如果沒有理由來掃描所有這些區域,則應該將範圍調整為涵蓋較少區域。 如果將資源範圍最小化,但仍使用許多區域,則可能是設定錯誤所造成。 例如,稽核記錄和診斷設定可能會傳送至不同區域中的不同工作區,或可能有多個診斷設定組態。
跨越超過 3 個區域的查詢會被視為耗用過多資源的查詢。 跨越超過 6 個區域的查詢會被視為濫用查詢,而且可能會對其進行節流。
重要
當查詢跨數個區域執行時,CPU 和資料量值不會精確,而且只會代表其中一個區域的量值。
工作區數目
工作區是用來隔離和管理記錄資料的邏輯容器。 後端會最佳化所選取區域內實體叢集上的工作區位置。
在某些情況下,可能會使用多個工作區:
- 已明確列出數個工作區。
- 資源範圍查詢正在擷取資料,而該資料存放於多個工作區中。
跨區域和跨叢集執行查詢需要系統在後端大型中繼資料區塊中序列化和傳輸,而這些區塊通常會略大於查詢最終結果。 這也會限制系統執行最佳化、啟發學習法和利用快取的能力。
跨越超過 5 個工作區的查詢會被視為耗用過多資源的查詢。 查詢不能跨越 100 個以上的工作區。
重要
- 在某些多工作區案例中,CPU 和資料量值將不會精確,而且只代表幾個工作區的量值。
- 具有明確識別碼的跨工作區查詢:工作區識別碼或工作區 Azure 資源識別碼,會耗用較少的資源,而且效能會更好。
如需詳細資訊,請參閱 跨資源查詢。
平行性
Azure 監視器記錄會使用 Azure Data Explorer 的大型叢集來執行查詢。 這些叢集會隨著規模而有所不同,而且可能會達到數十個計算節點。 系統會根據工作區放置邏輯和容量來自動調整叢集。
為了有效率地執行查詢,系統會根據處理查詢所需的資料,將查詢分割並散發至計算節點。 在某些情況下,系統無法有效率地執行此步驟,這可能會導致查詢的持續時間較長。
可減少平行處理原則的查詢行為包括:
- 在許多情況下,連接和聚合等操作符會降低整體並行性。 如果效能出現問題,請考慮使用重新排序。 當索引鍵 (換句話說,用於 join 或 summarize 的資料行) 具有高基數時,請使用 shuffle。 舉例來說,當資料行包含公用 IP 位址時應使用 shuffle。 請避免針對基數低的索引鍵 (例如事件的嚴重性層級) 使用 shuffle。
- 使用序列化和視窗函式,例如 serialize 運算子、next()、prev() 和 row 函式。 時間序列和使用者分析函數可以用於其中一些案例。 如果未在查詢結尾使用下列運算子,則也可能會發生無效率的序列化:range、sort、order、top、top-hitters 和 getschema。
- 使用 dcount() 彙總函式可強制系統具有相異值的中央複本。 資料規模很高時,請考慮使用
dcount函式選用參數來降低精確度。 - 在資源範圍的查詢中,如果有很大量的 Azure 角色指派,則可能會停留在執行前的 Kubernetes 角色型存取控制 (RBAC) 或 Azure RBAC 檢查。 此情況可能會導致檢查時間較長,進而造成較低的平行度。 例如,查詢可能會在訂用帳戶上執行,其中有數千個資源,而且每個資源的資源層級中都有許多角色指派,而不是在訂用帳戶或資源群組上。
- 如果查詢處理少量的資料區塊,則平行性會很低,因為系統不會將查詢分散到許多計算節點。
記憶體尖峰
記憶體峰值是 Azure 資料總管引擎在執行查詢時觀察到的最大 RAM 數量。 這包括用於資料載入 (快取/經常性讀取)、運算子處理 (例如 join、summarize、make-series) 及暫時工作集的記憶體。 這是導致記憶體失控狀況的前導指標,可能觸發例如失控查詢 (E_RUNAWAY_QUERY,運算子超出記憶體預算) 或 E_LOW_MEMORY_CONDITION 等防護。 監視記憶體峰值可協助及早偵測這些模式,並在達到相關硬性限制前調整查詢。
如何降低記憶體峰值
- 請遵循 CPU 總計 中所述的相同做法。 具體來說,請對查詢中的每個資料表套用記錄的早期篩選與資料行的投影。
- 在許多情況下,例如 join 和 summarize 此類運算子會導致高記憶體使用量,可能造成失控查詢。 如果效能出現問題,請考慮使用重新排序。 當索引鍵 (換句話說,用於 join 或 summarize 的資料行) 具有高基數時,請使用 shuffle。 舉例來說,當資料行包含公用 IP 位址時應使用 shuffle。 請避免針對基數低的索引鍵 (例如事件的嚴重性層級) 使用 shuffle。
- 若使用 join,請在適用時遵循最佳做法。 請參閱查詢最佳做法。
- 請考慮使用取樣。