Optimize Your Kusto Queries Like a Pro - Part 1

 How to Optimize Your Kusto Queries Like a Pro

If you're like me, you love using Azure Sentinel to monitor and analyze your data. But sometimes, you might find yourself frustrated by the performance of your Kusto queries. Maybe they take too long to run, or consume too much resources, or return too much data. Don't worry, I've got you covered. In this blog post, I'll share some tips and tricks on how to optimize your Kusto queries like a pro.


Tip #1: Filter early and often


One of the most common mistakes I see people make is to apply filters at the end of their queries, instead of at the beginning. This means that they are processing more data than they need to, which wastes time and resources. For example, let's say you want to find all the events from a specific device in the last 24 hours. You might write something like this:


kql

union *

| where TimeGenerated > ago(24h)

| where DeviceName == "MyDevice"

```


This query will scan all the tables in your workspace, and then filter them by time and device name. But a better way to write this query is to use the `in` operator to specify which tables you want to query, and then apply the filters as soon as possible. For example:


```kql

union SecurityEvent, Syslog, WindowsEvent

| where TimeGenerated > ago(24h) and DeviceName == "MyDevice"

```


This query will only scan the three tables that are relevant for your query, and then filter them by time and device name. This will reduce the amount of data that needs to be processed, and improve the performance of your query.


Tip #2: Use summarization functions wisely


Another common mistake I see people make is to use summarization functions without specifying a time window or a group by clause. This means that they are aggregating all the data in their query, which can be very expensive and unnecessary. For example, let's say you want to find the average CPU usage of your devices in the last 24 hours. You might write something like this:


```kql

union *

| where TimeGenerated > ago(24h)

| summarize avg(CPU)

```


This query will scan all the tables in your workspace, filter them by time, and then calculate the average CPU usage of all the events. But this is not very useful, because you don't know how the CPU usage varies over time or across different devices. A better way to write this query is to use a time window and a group by clause to break down the data into more meaningful segments. For example:


```kql

union *

| where TimeGenerated > ago(24h)

| summarize avg(CPU) by bin(TimeGenerated, 1h), DeviceName

```


This query will scan all the tables in your workspace, filter them by time, and then calculate the average CPU usage of each device for each hour. This will give you more insight into how the CPU usage changes over time and across different devices.


Tip #3: Use joins sparingly


The last mistake I want to talk about is using joins too frequently or unnecessarily. Joins are powerful operations that allow you to combine data from different tables based on a common key. But they can also be very expensive and complex, especially if you join large tables or multiple tables at once. For example, let's say you want to find all the events from a specific device that have an alert associated with them. You might write something like this:


```kql

union *

| where TimeGenerated > ago(24h) and DeviceName == "MyDevice"

| join kind=inner (

SecurityAlert

| where TimeGenerated > ago(24h)

) on $left.DeviceName == $right.DeviceName

```


This query will scan all the tables in your workspace, filter them by time and device name, and then join them with the SecurityAlert table based on the device name. This can be very slow and resource-intensive, especially if you have a lot of events or alerts in your workspace. A better way to write this query is to use a subquery instead of a join. For example:


```kql

union *

| where TimeGenerated > ago(24h) and DeviceName == "MyDevice"

and DeviceName in (

SecurityAlert

| where TimeGenerated > ago(24h)

| summarize by DeviceName

)

```


This query will scan all the tables in your workspace, filter them by time and device name, and then check if the device name exists in a subquery that returns only the distinct device names from the SecurityAlert table. This will