Поделиться через


Присоединение к окну времени

Применимо: ✅Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft Sentinel

Часто полезно объединить между двумя большими наборами данных в некоторых ключах с высоким кратностью, например идентификатором операции или идентификатором сеанса, а также дополнительно ограничить правой стороны ($right) записи, которые должны соответствовать каждой левой ($left) записи, добавив ограничение на "расстояние времени" между datetime столбцами слева и справа.

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

Заметка

Функция расстояния не ведет себя как равенство (т. е. когда оба dist(x,y) и dist(y,z) не соответствуют тому, что dist(x,z) также верно.) Иногда это называется "диагональным соединением".

Пример определения последовательностей событий без периода времени

Чтобы определить последовательности событий в относительно небольшом окне времени, в этом примере используется таблица T со следующей схемой:

  • SessionId: столбец типа string с идентификаторами корреляции.
  • EventType: столбец типа string, определяющий тип события записи.
  • Timestamp: столбец типа datetime указывает, когда произошло событие, описанное записью.
SessionId EventType Метка времени
0 A 2017-10-01T00:00:00Z
0 B 2017-10-01T00:01:00Z
1 B 2017-10-01T00:02:00Z
1 A 2017-10-01T00:03:00Z
3 A 2017-10-01T00:04:00Z
3 B 2017-10-01T00:10:00Z

Следующий запрос создает набор данных, а затем идентифицирует все идентификаторы сеансов, за которыми следует тип события AB в течение 1min периода времени.

let T = datatable(SessionId:string, EventType:string, Timestamp:datetime)
[
    '0', 'A', datetime(2017-10-01 00:00:00),
    '0', 'B', datetime(2017-10-01 00:01:00),
    '1', 'B', datetime(2017-10-01 00:02:00),
    '1', 'A', datetime(2017-10-01 00:03:00),
    '3', 'A', datetime(2017-10-01 00:04:00),
    '3', 'B', datetime(2017-10-01 00:10:00),
];
T
| where EventType == 'A'
| project SessionId, Start=Timestamp
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Timestamp
    ) on SessionId
| where (End - Start) between (0min .. 1min)
| project SessionId, Start, End 

выходных

SessionId Начало Конец
0 2017-10-01 00:00:00.0000000 2017-10-01 00:01:00.0000000

Пример, оптимизированный с периодом времени

Чтобы оптимизировать этот запрос, можно переписать его в учетную запись периода времени. Интервал времени THe выражается как ключ соединения. Переопределите запрос, чтобы значения datetime были "дискретизированы" в контейнеры, размер которых составляет половину периода времени. Используйте equi-join для сравнения идентификаторов контейнеров.

Запрос находит пары событий в одном сеансе (SessionId), где за событием A следует событие "B" в течение 1 минуты. Он проектит идентификатор сеанса, время начала события "A" и время окончания события "B".

let T = datatable(SessionId:string, EventType:string, Timestamp:datetime)
[
    '0', 'A', datetime(2017-10-01 00:00:00),
    '0', 'B', datetime(2017-10-01 00:01:00),
    '1', 'B', datetime(2017-10-01 00:02:00),
    '1', 'A', datetime(2017-10-01 00:03:00),
    '3', 'A', datetime(2017-10-01 00:04:00),
    '3', 'B', datetime(2017-10-01 00:10:00),
];
let lookupWindow = 1min;
let lookupBin = lookupWindow / 2.0;
T 
| where EventType == 'A'
| project SessionId, Start=Timestamp, TimeKey = bin(Timestamp, lookupBin)
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Timestamp,
              TimeKey = range(bin(Timestamp-lookupWindow, lookupBin),
                              bin(Timestamp, lookupBin),
                              lookupBin)
    | mv-expand TimeKey to typeof(datetime)
    ) on SessionId, TimeKey 
| where (End - Start) between (0min .. lookupWindow)
| project SessionId, Start, End 

выходных

SessionId Начало Конец
0 2017-10-01 00:00:00.0000000 2017-10-01 00:01:00.0000000

5 миллионов запросов к данным

Следующий запрос эмулирует обширный набор данных из 5M записей и приблизительно 1М идентификаторов сеансов и выполняет запрос с помощью метода периода времени.

let T = range x from 1 to 5000000 step 1
| extend SessionId = rand(1000000), EventType = rand(3), Time=datetime(2017-01-01)+(x * 10ms)
| extend EventType = case(EventType < 1, "A",
                          EventType < 2, "B",
                          "C");
let lookupWindow = 1min;
let lookupBin = lookupWindow / 2.0;
T 
| where EventType == 'A'
| project SessionId, Start=Time, TimeKey = bin(Time, lookupBin)
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Time, 
              TimeKey = range(bin(Time-lookupWindow, lookupBin), 
                              bin(Time, lookupBin),
                              lookupBin)
    | mv-expand TimeKey to typeof(datetime)
    ) on SessionId, TimeKey 
| where (End - Start) between (0min .. lookupWindow)
| project SessionId, Start, End 
| count 

выходных

Считать
3344
  • оператор соединения