Sdílet prostřednictvím


Použití funkcí hll() a tdigest()

Platí pro: ✅Microsoft FabricAzure Data Explorer✅Azure MonitorMicrosoft Sentinel

Předpokládejme, že chcete vypočítat počet jedinečných uživatelů každý den za posledních 7 dnů. Můžete spustit summarize dcount(user) jednou denně s rozsahem filtrovaným na posledních 7 dnů. Tato metoda je neefektivní, protože při každém spuštění výpočtu se šestidenní překrývání s předchozím výpočtem. Můžete také vypočítat agregaci pro každý den a pak tyto agregace zkombinovat. Tato metoda vyžaduje, abyste si "vzpomněli" na posledních šest výsledků, ale je mnohem efektivnější.

Dělení dotazů, jak je popsáno, je snadné pro jednoduché agregace, například count() a sum(). Může být také užitečné pro komplexní agregace, například dcount() a percentiles(). Tento článek vysvětluje, jak Kusto podporuje takové výpočty.

Následující příklady ukazují, jak používat hll/tdigest a demonstrovat, že použití těchto příkazů je v některých scénářích vysoce výkonné:

Důležité

Výsledky hll, , hll_mergehll_if, tdigesta tdigest_merge jsou objekty typudynamic, které pak mohou být zpracovány jinými funkcemi (dcount_hll, percentile_tdigest, percentiles_array_tdigesta percentrank_tdigest). Kódování tohoto objektu se může v průběhu času změnit (například kvůli upgradu softwaru); Tyto změny se však budou provádět zpětně kompatibilním způsobem, takže je můžete trvale ukládat a odkazovat na ně v dotazech spolehlivě.

Poznámka:

V některých případech můžou být dynamické objekty generované hll agregačními tdigest funkcemi velké a překračují výchozí vlastnost MaxValueSize v zásadách kódování. Pokud ano, objekt se ingestuje jako null. Například při zachování výstupu hll funkce s přesností úrovně 4 překračuje velikost hll objektu výchozí MaxValueSize, což je 1 MB. Chcete-li se tomuto problému vyhnout, upravte zásady kódování sloupce, jak je znázorněno v následujících příkladech.

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

Výstup

sizeInMb
1.0000524520874

Ingestování tohoto objektu do tabulky před použitím tohoto typu zásady bude ingestovat hodnotu null:

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

Výstup

Column1
0

Pokud se chcete vyhnout ingestování hodnoty null, použijte speciální typ bigobjectzásad kódování, který přepíše MaxValueSize hodnotu 2 MB takto:

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

Ingestování hodnoty teď do stejné tabulky výše:

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

ingestuje druhou hodnotu úspěšně:

MyTable
| project isempty(hll_x)

Výstup

Column1
0
0

Příklad: Count with binned timestamp

Existuje tabulka, PageViewsHllTDigestkterá obsahuje hll hodnoty stránek zobrazených v každé hodině. Chcete, aby 12htyto hodnoty byly přihrádky . Sloučí hll hodnoty pomocí hll_merge() agregační funkce s časovým razítkem v 12hintervalu . dcount_hll Funkce slouží k vrácení konečné dcount hodnoty:

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

Výstup

Časové razítko 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

Časové razítko přihrádky intervalu pro 1d:

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

Výstup

Časové razítko 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

Stejný dotaz může být proveden v hodnotách tdigest, které představují BytesDelivered za každou hodinu:

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

Výstup

Časové razítko 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

Příklad: Dočasná tabulka

Limity Kusto jsou dosaženy u datových sad, které jsou příliš velké, kde potřebujete spouštět pravidelné dotazy na datovou sadu, ale spouštět běžné dotazy pro výpočet percentile() nebo dcount() nad velkými datovými sadami.

Chcete-li tento problém vyřešit, mohou být nově přidaná data přidána do dočasné tabulky jako hll nebo hodnoty pomocíhll(), pokud je dcount požadovaná operace nebo tdigest() pokud je požadovaná operace percentil pomocí set/append nebo update policytdigest . V tomto případě se přechodné výsledky dcount nebo tdigest uloží do jiné datové sady, které by měly být menší než cílové velké.

Chcete-li tento problém vyřešit, mohou být nově přidaná data přidána do dočasné tabulky jako hll nebo hodnoty pomocíhll(), pokud je dcountpožadovaná tdigest operace . V tomto případě se přechodné výsledky dcount uloží do jiné datové sady, která by měla být menší než cílová velká.

Pokud potřebujete získat konečné výsledky těchto hodnot, mohou dotazy použít hll/tdigest fúze: . hll-merge()/tdigest_merge() Po získání sloučených hodnot percentile_tdigest() / dcount_hll() je pak možné u těchto sloučených hodnot vyvolat výsledek konečného výsledku dcount nebo percentilů.

Za předpokladu, že existuje tabulka PageViews, do které se data ingestují denně, každý den, kdy chcete vypočítat jedinečný počet stránek zobrazených za minutu než datum = datetime(2016-05-01 18:00:00,00,000000).

Spusťte tento dotaz:

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

Výstup

Časové razítko 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

Tento dotaz agreguje všechny hodnoty při každém spuštění tohoto dotazu (například pokud ho chcete spustit mnohokrát denně).

Pokud uložíte hll hodnoty a tdigest hodnoty (což jsou přechodné výsledky dcount a percentil) do dočasné tabulky, PageViewsHllTDigestpomocí aktualizačních zásad nebo příkazů set/append, můžete sloučit pouze hodnoty a pak použít dcount_hll/percentile_tdigest následující dotaz:

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

Výstup

Časové razítko 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

Tento dotaz by měl být výkonnější, protože běží přes menší tabulku. V tomto příkladu první dotaz běží přes přibližně 215M záznamů, zatímco druhý dotaz běží přes pouhých 32 záznamů:

Příklad: Průběžné výsledky

Dotaz uchovávání informací. Předpokládejme, že máte tabulku, která shrnuje, kdy se zobrazila každá stránka Wikipedie (velikost vzorku je 10 M) a chcete pro každé datum 1 datum2 najít procento stránek zkontrolovaných v date1 i date2 vzhledem ke stránkám zobrazeným k datu 1 (datum1 < datum2).

Triviální způsob používá operátory spojení a sumarizace:

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

Výstup

Den 1 Den 2 Procento
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

Výše uvedený dotaz trval přibližně 18 sekund.

Když použijete funkci hll(), hll_merge()a dcount_hll() funkce, ekvivalentní dotaz skončí po ~1,3 sekundách a ukáže, že hll funkce urychlí dotaz výše o ~14krát:

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

Výstup

den 1 den2 Procento
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

Poznámka:

Výsledky dotazů nejsou 100% přesné kvůli chybě hll funkcí. Další informace o chybách naleznete v tématu dcount().