Partage via


Utilisation de hll() et de tdigest()

S’applique à : ✅Microsoft Fabric✅Azure Data ExplorerAzure MonitorMicrosoft Sentinel

Supposons que vous souhaitez calculer le nombre d’utilisateurs distincts tous les jours au cours des sept derniers jours. Vous pouvez exécuter summarize dcount(user) une fois par jour avec une étendue filtrée jusqu’aux sept derniers jours. Cette méthode est inefficace, car chaque fois que le calcul est exécuté, il existe un chevauchement de six jours avec le calcul précédent. Vous pouvez également calculer un agrégat pour chaque jour, puis combiner ces agrégats. Cette méthode vous oblige à « mémoriser » les six derniers résultats, mais il est beaucoup plus efficace.

Le partitionnement des requêtes comme décrit est facile pour les agrégats simples, tels que count() et sum(). Il peut également être utile pour les agrégats complexes, tels que dcount() et percentiles(). Cet article explique comment Kusto prend en charge ces calculs.

Les exemples suivants montrent comment utiliser hll/tdigest et démontrer que l’utilisation de ces commandes est très performante dans certains scénarios :

Important

Les résultats de hll, hll_iftdigesthll_mergeet tdigest_merge sont des objets de type dynamic qui peuvent ensuite être traités par d’autres fonctions (dcount_hll, , percentiles_array_tdigestpercentile_tdigestet ).percentrank_tdigest L’encodage de cet objet peut changer au fil du temps (par exemple, en raison d’une mise à niveau logicielle) ; Toutefois, ces modifications seront effectuées de manière rétrocompatible, afin qu’elles puissent stocker ces valeurs de manière persistante et les référencer de manière fiable dans les requêtes.

Remarque

Dans certains cas, les objets dynamiques générés par les hll fonctions d’agrégation peuvent tdigest être volumineux et dépasser la propriété MaxValueSize par défaut dans la stratégie d’encodage. Dans ce cas, l’objet est ingéré comme null. Par exemple, lors de la persistance de la sortie de hll la fonction avec le niveau de précision 4, la taille de l’objet hll dépasse la valeur MaxValueSize par défaut, qui est de 1 Mo. Pour éviter ce problème, modifiez la stratégie d’encodage de la colonne, comme indiqué dans les exemples suivants.

range x from 1 to 1000000 step 1
| summarize hll(x,4)
| project sizeInMb = estimate_data_size(hll_x) / pow(1024,2)

Sortie

sizeInMb
1.0000524520874

L’ingestion de cet objet dans une table avant d’appliquer ce type de stratégie ingère null :

.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)
MyTable
| project isempty(hll_x)

Sortie

Column1
1

Pour éviter d’ingérer null, utilisez le type bigobjectde stratégie d’encodage spécial, qui remplace la MaxValueSize valeur de 2 Mo comme suit :

.alter column MyTable.hll_x policy encoding type='bigobject'

Ingestion d’une valeur maintenant dans le même tableau ci-dessus :

.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)

ingestion de la deuxième valeur avec succès :

MyTable
| project isempty(hll_x)

Sortie

Column1
1
0

Exemple : Nombre avec horodatage binned

Il existe une table, PageViewsHllTDigestcontenant des hll valeurs de Pages affichées toutes les heures. Vous souhaitez que ces valeurs soient binnées 12h. Fusionnez les hll valeurs à l’aide de la fonction d’agrégation hll_merge() , avec l’horodatage binned vers 12h. Utilisez la fonction dcount_hll pour retourner la valeur finale dcount :

PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 12h)
| project Timestamp , dcount_hll(merged_hll)

Sortie

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

Pour bin timestamp pour 1d:

PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 1d)
| project Timestamp , dcount_hll(merged_hll)

Sortie

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

La même requête peut être effectuée sur les valeurs de tdigest, qui représentent le BytesDelivered dans chaque heure :

PageViewsHllTDigest
| summarize merged_tdigests = merge_tdigest(tdigestBytesDel) by bin(Timestamp, 12h)
| project Timestamp , percentile_tdigest(merged_tdigests, 95, typeof(long))

Sortie

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

Exemple : table temporaire

Les limites Kusto sont atteintes avec des jeux de données trop volumineux, où vous devez exécuter des requêtes périodiques sur le jeu de données, mais exécuter les requêtes régulières pour calculer ou dcount() sur percentile() des jeux de données volumineux.

Pour résoudre ce problème, les données nouvellement ajoutées peuvent être ajoutées à une table temporaire en tant que table temporaire en tant que hll valeurs tdigest à l’aide hll() du moment où l’opération requise est dcount ou tdigest() lorsque l’opération requise est centile à l’aide set/append ou update policy. Dans ce cas, les résultats intermédiaires d’un tdigest autre jeu de dcount données doivent être inférieurs à ceux de la cible.

Pour résoudre ce problème, les données nouvellement ajoutées peuvent être ajoutées à une table temporaire en tant que ou tdigest en tant que hll valeurs à l’aide hll() de l’opération dcountrequise. Dans ce cas, les résultats intermédiaires sont dcount enregistrés dans un autre jeu de données, qui doit être inférieur à celui de la cible grande.

Lorsque vous devez obtenir les résultats finaux de ces valeurs, les requêtes peuvent utiliser hll/tdigest des fusions : hll-merge()/tdigest_merge(). Ensuite, après avoir obtenu les valeurs fusionnées, percentile_tdigest() / dcount_hll() peut être appelée sur ces valeurs fusionnées pour obtenir le résultat final des dcount centiles ou des centiles.

En supposant qu’il existe une table, PageViews, dans laquelle les données sont ingérées quotidiennement, chaque jour sur lequel vous souhaitez calculer le nombre distinct de pages affichées par minute plus tard que la date = datetime(2016-05-01 18:00:00.00000000).

Exécutez la requête suivante :

PageViews
| where Timestamp > datetime(2016-05-01 18:00:00.0000000)
| summarize percentile(BytesDelivered, 90), dcount(Page,2) by bin(Timestamp, 1d)

Sortie

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

Cette requête agrège toutes les valeurs chaque fois que vous exécutez cette requête (par exemple, si vous souhaitez l’exécuter plusieurs fois par jour).

Si vous enregistrez les hll valeurs tdigest (qui sont les résultats intermédiaires et dcount le centile) dans une table temporaire, PageViewsHllTDigestà l’aide d’une stratégie de mise à jour ou de commandes set/append, vous pouvez uniquement fusionner les valeurs, puis utiliser dcount_hll/percentile_tdigest la requête suivante :

PageViewsHllTDigest
| summarize  percentile_tdigest(merge_tdigest(tdigestBytesDel), 90), dcount_hll(hll_merge(hllPage)) by bin(Timestamp, 1d)

Sortie

Timestamp percentile_tdigest_merge_tdigests_tdigestBytesDel dcount_hll_hll_merge_hllPage
2016-05-01 00:00:00.0000000 84,224 20056275
2016-05-02 00:00:00.0000000 83486 64135183
2016-05-03 00:00:00.0000000 72247 13685621

Cette requête doit être plus performante, car elle s’exécute sur une table plus petite. Dans cet exemple, la première requête s’exécute sur les enregistrements ~215M, tandis que la seconde s’exécute sur seulement 32 enregistrements :

Exemple : résultats intermédiaires

Requête de rétention. Supposons que vous disposez d’un tableau qui résume le moment où chaque page Wikipédia a été affichée (la taille de l’échantillon est de 10M) et que vous souhaitez rechercher pour chaque date1 date2 le pourcentage de pages examinées à la fois dans date1 et date2 par rapport aux pages affichées à date1 (date1 < date2).

La façon triviale utilise les opérateurs de jointure et de synthèse :

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

Sortie

Jour1 Jour2 Pourcentage
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 requête ci-dessus a pris environ 18 secondes.

Lorsque vous utilisez , hll()hll_merge()et dcount_hll() les fonctions, la requête équivalente se termine après ~1,3 secondes et montre que les hll fonctions accélèrent la requête ci-dessus d’environ 14 fois :

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

Sortie

day1 jour2 Pourcentage
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

Remarque

Les résultats des requêtes ne sont pas exacts à 100 % en raison de l’erreur hll des fonctions. Pour plus d’informations sur les erreurs, consultez dcount().