Uso di hll() e tdigest()
Si applica a: ✅Microsoft Fabric✅Azure Esplora dati✅ Azure Monitor✅Microsoft Sentinel
Si supponga di voler calcolare il numero di utenti distinti ogni giorno negli ultimi sette giorni. È possibile eseguire summarize dcount(user)
una volta al giorno con un intervallo filtrato negli ultimi sette giorni. Questo metodo è inefficiente, perché ogni volta che viene eseguito il calcolo, si verifica una sovrapposizione di sei giorni con il calcolo precedente. È anche possibile calcolare un'aggregazione per ogni giorno e quindi combinare queste aggregazioni. Questo metodo richiede di "ricordare" gli ultimi sei risultati, ma è molto più efficiente.
Il partizionamento delle query come descritto è semplice per le aggregazioni semplici, ad esempio count()
e sum()
. Può anche essere utile per aggregazioni complesse, ad esempio dcount()
e percentiles()
. Questo articolo illustra come Kusto supporta tali calcoli.
Gli esempi seguenti illustrano come usare hll
/tdigest
e dimostrare che l'uso di questi comandi è estremamente efficiente in alcuni scenari:
Importante
I risultati di hll
, hll_if
hll_merge
, tdigest
, e tdigest_merge
sono oggetti di tipo dynamic
che possono quindi essere elaborati da altre funzioni (dcount_hll
, percentile_tdigest
, percentiles_array_tdigest
e percentrank_tdigest
). La codifica di questo oggetto potrebbe cambiare nel tempo (ad esempio, a causa di un aggiornamento software); Tuttavia, tali modifiche verranno eseguite in modo compatibile con le versioni precedenti, in modo che sia possibile archiviare tali valori in modo permanente e farvi riferimento in modo affidabile nelle query.
Nota
In alcuni casi, gli oggetti dinamici generati dalle hll
funzioni di aggregazione o tdigest
possono essere grandi e superare la proprietà MaxValueSize predefinita nei criteri di codifica. In tal caso, l'oggetto verrà inserito come Null.
Ad esempio, quando si rende persistente l'output della hll
funzione con livello di accuratezza 4, le dimensioni dell'oggetto hll
superano il valore predefinito MaxValueSize, ovvero 1 MB.
Per evitare questo problema, modificare i criteri di codifica della colonna, come illustrato negli esempi seguenti.
range x from 1 to 1000000 step 1
| summarize hll(x,4)
| project sizeInMb = estimate_data_size(hll_x) / pow(1024,2)
Output
sizeInMb |
---|
1.0000524520874 |
L'inserimento di questo oggetto in una tabella prima dell'applicazione di questo tipo di criteri ingestirà null:
.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)
MyTable
| project isempty(hll_x)
Output
Colonna1 |
---|
1 |
Per evitare l'inserimento di valori Null, usare il tipo di criterio di bigobject
codifica speciale , che esegue l'override di MaxValueSize
su 2 MB come segue:
.alter column MyTable.hll_x policy encoding type='bigobject'
Inserimento di un valore nella stessa tabella precedente:
.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)
inserisce correttamente il secondo valore:
MyTable
| project isempty(hll_x)
Output
Colonna1 |
---|
1 |
0 |
Esempio: Conteggio con timestamp binned
È presente una tabella, PageViewsHllTDigest
, contenente hll
i valori di Pages visualizzati in ogni ora. Si desidera che questi valori vengano binati in 12h
. Unire i hll
valori usando la hll_merge()
funzione di aggregazione, con il timestamp binned in 12h
. Usare la funzione dcount_hll
per restituire il valore finale dcount
:
PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 12h)
| project Timestamp , dcount_hll(merged_hll)
Output
Timestamp: | dcount_hll_merged_hll |
---|---|
2016-05-01 12:00:00.0000000 | 20056275 |
2016-05-02 00:00:00.0000000 | 38797623 |
2016-05-02 12:00:00.0000000 | 39316056 |
2016-05-03 00:00:00.0000000 | 13685621 |
Per binare il timestamp per 1d
:
PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 1d)
| project Timestamp , dcount_hll(merged_hll)
Output
Timestamp: | dcount_hll_merged_hll |
---|---|
2016-05-01 00:00:00.0000000 | 20056275 |
2016-05-02 00:00:00.0000000 | 64135183 |
2016-05-03 00:00:00.0000000 | 13685621 |
È possibile eseguire la stessa query sui valori di tdigest
, che rappresentano l'oggetto BytesDelivered
in ogni ora:
PageViewsHllTDigest
| summarize merged_tdigests = merge_tdigest(tdigestBytesDel) by bin(Timestamp, 12h)
| project Timestamp , percentile_tdigest(merged_tdigests, 95, typeof(long))
Output
Timestamp: | percentile_tdigest_merged_tdigests |
---|---|
2016-05-01 12:00:00.0000000 | 170200 |
2016-05-02 00:00:00.0000000 | 152975 |
2016-05-02 12:00:00.0000000 | 181315 |
2016-05-03 00:00:00.0000000 | 146817 |
Esempio: Tabella temporanea
I limiti Kusto vengono raggiunti con set di dati troppo grandi, in cui è necessario eseguire query periodiche sul set di dati, ma eseguire le normali query per calcolare percentile()
o dcount()
superare set di dati di grandi dimensioni.
Per risolvere questo problema, i dati appena aggiunti possono essere aggiunti a una tabella temporanea come hll
valori o tdigest
usando hll()
quando l'operazione richiesta è dcount
o tdigest()
quando l'operazione richiesta è percentile usando set/append
o update policy
. In questo caso, i risultati intermedi di o tdigest
vengono salvati in un altro set di dcount
dati, che deve essere inferiore a quello di destinazione di grandi dimensioni.
Per risolvere questo problema, i dati appena aggiunti possono essere aggiunti a una tabella temporanea come hll
o valori usando hll()
quando l'operazione richiesta è dcount
tdigest
. In questo caso, i risultati intermedi di vengono salvati in un altro set di dcount
dati, che deve essere inferiore a quello di destinazione di grandi dimensioni.
Quando è necessario ottenere i risultati finali di questi valori, le query possono usare hll
/tdigest
fusioni: . hll-merge()
/tdigest_merge()
Quindi, dopo aver ottenuto i valori uniti, percentile_tdigest()
/ dcount_hll()
può essere richiamato su questi valori uniti per ottenere il risultato finale di dcount
o percentili.
Supponendo che sia presente una tabella, PageViews, in cui i dati vengono inseriti ogni giorno, ogni giorno in cui si desidera calcolare il numero distinto di pagine visualizzate al minuto dopo date = datetime(2016-05-01 18:00:00.00.0000000).
Eseguire la query riportata di seguito:
PageViews
| where Timestamp > datetime(2016-05-01 18:00:00.0000000)
| summarize percentile(BytesDelivered, 90), dcount(Page,2) by bin(Timestamp, 1d)
Output
Timestamp: | percentile_BytesDelivered_90 | dcount_Page |
---|---|---|
2016-05-01 00:00:00.0000000 | 83634 | 20056275 |
2016-05-02 00:00:00.0000000 | 82770 | 64135183 |
2016-05-03 00:00:00.0000000 | 72920 | 13685621 |
Questa query aggrega tutti i valori ogni volta che si esegue la query, ad esempio se si vuole eseguirla più volte al giorno.
Se si salvano i hll
valori e tdigest
(ovvero i risultati intermedi di dcount
e percentile) in una tabella temporanea, , PageViewsHllTDigest
usando un criterio di aggiornamento o i comandi set/append, è possibile unire solo i valori e quindi usare dcount_hll
/percentile_tdigest
la query seguente:
PageViewsHllTDigest
| summarize percentile_tdigest(merge_tdigest(tdigestBytesDel), 90), dcount_hll(hll_merge(hllPage)) by bin(Timestamp, 1d)
Output
Timestamp: | percentile_tdigest_merge_tdigests_tdigestBytesDel |
dcount_hll_hll_merge_hllPage |
---|---|---|
2016-05-01 00:00:00.0000000 | 84224 | 20056275 |
2016-05-02 00:00:00.0000000 | 83486 | 64135183 |
2016-05-03 00:00:00.0000000 | 72247 | 13685621 |
Questa query dovrebbe essere più efficiente, perché viene eseguita su una tabella più piccola. In questo esempio la prima query viene eseguita su ~215M record, mentre la seconda viene eseguita su soli 32 record:
Esempio: Risultati intermedi
Query di conservazione. Si supponga di avere una tabella che riepiloga quando è stata visualizzata ogni pagina di Wikipedia (la dimensione del campione è 10M) e si vuole trovare per ogni data1 data2 la percentuale di pagine esaminate sia in date1 che in data2 rispetto alle pagine visualizzate in data1 (data1 < data2).
Il modo semplice usa operatori join e summarize:
// Get the total pages viewed each day
let totalPagesPerDay = PageViewsSample
| summarize by Page, Day = startofday(Timestamp)
| summarize count() by Day;
// Join the table to itself to get a grid where
// each row shows foreach page1, in which two dates
// it was viewed.
// Then count the pages between each two dates to
// get how many pages were viewed between date1 and date2.
PageViewsSample
| summarize by Page, Day1 = startofday(Timestamp)
| join kind = inner
(
PageViewsSample
| summarize by Page, Day2 = startofday(Timestamp)
)
on Page
| where Day2 > Day1
| summarize count() by Day1, Day2
| join kind = inner
totalPagesPerDay
on $left.Day1 == $right.Day
| project Day1, Day2, Percentage = count_*100.0/count_1
Output
Giorno1 | Giorno 2 | Percentuale |
---|---|---|
2016-05-01 00:00:00.0000000 | 2016-05-02 00:00:00.0000000 | 34,0645725975255 |
2016-05-01 00:00:00.0000000 | 2016-05-03 00:00:00.0000000 | 16,618368960101 |
2016-05-02 00:00:00.0000000 | 2016-05-03 00:00:00.0000000 | 14,6291376489636 |
La query precedente ha richiesto circa 18 secondi.
Quando si usano le hll()
funzioni , hll_merge()
e dcount_hll()
, la query equivalente terminerà dopo circa 1,3 secondi e mostrerà che le hll
funzioni accelerano la query precedente di circa 14 volte:
let Stats=PageViewsSample | summarize pagehll=hll(Page, 2) by day=startofday(Timestamp); // saving the hll values (intermediate results of the dcount values)
let day0=toscalar(Stats | summarize min(day)); // finding the min date over all dates.
let dayn=toscalar(Stats | summarize max(day)); // finding the max date over all dates.
let daycount=tolong((dayn-day0)/1d); // finding the range between max and min
Stats
| project idx=tolong((day-day0)/1d), day, pagehll
| mv-expand pidx=range(0, daycount) to typeof(long)
// Extend the column to get the dcount value from hll'ed values for each date (same as totalPagesPerDay from the above query)
| extend key1=iff(idx < pidx, idx, pidx), key2=iff(idx < pidx, pidx, idx), pages=dcount_hll(pagehll)
// For each two dates, merge the hll'ed values to get the total dcount over each two dates,
// This helps to get the pages viewed in both date1 and date2 (see the description below about the intersection_size)
| summarize (day1, pages1)=arg_min(day, pages), (day2, pages2)=arg_max(day, pages), union_size=dcount_hll(hll_merge(pagehll)) by key1, key2
| where day2 > day1
// To get pages viewed in date1 and also date2, look at the merged dcount of date1 and date2, subtract it from pages of date1 + pages on date2.
| project pages1, day1,day2, intersection_size=(pages1 + pages2 - union_size)
| project day1, day2, Percentage = intersection_size*100.0 / pages1
Output
day1 | day2 | Percentuale |
---|---|---|
2016-05-01 00:00:00.0000000 | 2016-05-02 00:00:00.0000000 | 33.2298494510578 |
2016-05-01 00:00:00.0000000 | 2016-05-03 00:00:00.0000000 | 16.9773830213667 |
2016-05-02 00:00:00.0000000 | 2016-05-03 00:00:00.0000000 | 14.5160020350006 |
Nota
I risultati delle query non sono accurati al 100% a causa dell'errore delle hll
funzioni. Per altre informazioni sugli errori, vedere dcount()
.