適用対象: ✅Microsoft Fabric✅Azure データ エクスプローラー✅Azure Monitor✅Microsoft Sentinel
過去 7 日間の個別のユーザーのカウントを毎日計算する必要があるとします。 過去 7 日間にスパン フィルターを適用して summarize dcount(user)
を 1 日 1 回実行することができます。 この方法は非効率です。計算を実行するたびに、前の計算と 6 日間の重複が発生するからです。 毎日の集計を計算してから、その集計を結合することもできます。 この方法では、最後の 6 つの結果を "記憶" する必要がありますが、はるかに効率は良くなります。
前述のパーティション分割クエリは、count()
や sum()
などの単純な集計では簡単です。 また、dcount()
や percentiles()
などの複雑な集計にも役立つ可能性があります。 この記事では、Kusto がこのような計算をサポートする方法について説明します。
次の例では、hll
/tdigest
を使用する方法を示し、これらのコマンドの使用が一部のシナリオで高い性能を示すことを実証します。
重要
hll
、hll_if
、hll_merge
、tdigest
、およびtdigest_merge
の結果は、他の関数 (dcount_hll
、percentile_tdigest
、percentiles_array_tdigest
、およびpercentrank_tdigest
) で処理できるdynamic
型のオブジェクトです。 このオブジェクトのエンコードは、時間の経過と伴って変化する可能性があります (ソフトウェアのアップグレードなど)。ただし、このような変更は下位互換性のある方法で行われるので、そのような値を永続的に格納し、クエリで確実に参照することができます。
Note
場合によっては、hll
または tdigest
集計関数によって生成される動的オブジェクトが、大きく、エンコード ポリシー内の既定の MaxValueSize プロパティを超える可能性があります。 その場合は、オブジェクトが null として取り込まれます。
たとえば、精度レベル 4 で hll
関数の出力を保持すると、hll
オブジェクトのサイズが既定の MaxValueSize (1 MB) を超えます。
この問題を回避するには、次の例に示すように、列のエンコード ポリシーを変更します。
range x from 1 to 1000000 step 1
| summarize hll(x,4)
| project sizeInMb = estimate_data_size(hll_x) / pow(1024,2)
出力
sizeInMb |
---|
1.0000524520874 |
この種類のポリシーを適用する前にこのオブジェクトをテーブルに取り込むと、null が取り込まれます。
.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)
MyTable
| project isempty(hll_x)
出力
列 1 |
---|
1 |
null の取り込みを回避するには、次のように、MaxValueSize
を 2 MB にオーバーライドする特殊なエンコード ポリシーの種類 bigobject
を使用します。
.alter column MyTable.hll_x policy encoding type='bigobject'
上記と同じテーブルへの値の取り込み:
.set-or-append MyTable <| range x from 1 to 1000000 step 1
| summarize hll(x,4)
2 つ目の値を正常に取り込む:
MyTable
| project isempty(hll_x)
出力
列 1 |
---|
1 |
0 |
例: ビン分割されたタイムスタンプを含むカウント
各時間に表示される Pages のhll
値を含むテーブルPageViewsHllTDigest
があります。 これらの値を 12h
にビン分割する必要があります。 集計関数 hll_merge()
を使用した hll
値を、12h
にビン分割されたタイムスタンプとマージします。 関数 dcount_hll
を使用して、最後の dcount
値を返します。
PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 12h)
| project Timestamp , dcount_hll(merged_hll)
出力
タイムスタンプ | 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 |
1d
のタイムスタンプをビン分割するには:
PageViewsHllTDigest
| summarize merged_hll = hll_merge(hllPage) by bin(Timestamp, 1d)
| project Timestamp , dcount_hll(merged_hll)
出力
タイムスタンプ | 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 |
1 時間ごとに BytesDelivered
を表す tdigest
の値に対して、同じクエリを実行できます。
PageViewsHllTDigest
| summarize merged_tdigests = merge_tdigest(tdigestBytesDel) by bin(Timestamp, 12h)
| project Timestamp , percentile_tdigest(merged_tdigests, 95, typeof(long))
出力
タイムスタンプ | 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 |
例: 一時テーブル
データセットが大きすぎると Kusto 制限に到達します。その場合は、データセットに対して定期的なクエリを実行する必要がありますが、大規模なデータセットに対して通常のクエリを実行して percentile()
または dcount()
を計算する必要があります。
この問題を解決するために、必要な操作が dcount
の場合は hll()
を使用し、必要な操作が set/append
または update policy
を使用したパーセンタイルの場合は tdigest()
を使用して、新しく追加されたデータを hll
または tdigest
の値として一時テーブルに追加できます。 この場合は、dcount
または tdigest
の中間結果が別のデータセットに保存されます。このデータセットは、ターゲットの大きいデータセットより小さくする必要があります。
この問題を解決するために、必要な操作が dcount
の場合に hll()
を使用して、新しく追加されたデータを hll
または tdigest
の値として一時テーブルに追加できます。 この場合は、dcount
の中間結果が別のデータセットに保存されます。このデータセットは、ターゲットの大きいデータセットより小さくする必要があります。
これらの値の最終結果を取得する必要がある場合は、クエリで hll
/tdigest
マージャーの hll-merge()
/tdigest_merge()
を使用できます。 その後で、マージされた値を取得してから、それらのマージされた値に対して percentile_tdigest()
/ dcount_hll()
を呼び出して、dcount
またはパーセンタイルの最終結果を取得できます。
毎日データが取り込まれるテーブル PageViews があり、datetime(2016-05-01 18:00:00.0000000) より後の日付に毎日 1 分ごとに表示されるページの個別のカウントを計算する必要があるとします。
次のクエリを実行します。
PageViews
| where Timestamp > datetime(2016-05-01 18:00:00.0000000)
| summarize percentile(BytesDelivered, 90), dcount(Page,2) by bin(Timestamp, 1d)
出力
タイムスタンプ | 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 |
このクエリは、このクエリを実行するたびにすべての値を集計します (たとえば、1 日に何度も実行する場合)。
更新ポリシーまたは set/append コマンドを使用して、hll
と tdigest
の値 (dcount
とパーセンタイルの中間結果) を一時テーブル PageViewsHllTDigest
に保存する場合は、値をマージしてから、次のクエリで dcount_hll
/percentile_tdigest
を使用するだけで済みます。
PageViewsHllTDigest
| summarize percentile_tdigest(merge_tdigest(tdigestBytesDel), 90), dcount_hll(hll_merge(hllPage)) by bin(Timestamp, 1d)
出力
タイムスタンプ | 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 |
このクエリは、小さめのテーブルに対して実行されるため、効率が良いはずです。 この例では、1 つ目のクエリが約 215M 件のレコードに対して実行され、2 つ目のクエリが 32 件のレコードに対して実行されます。
例: 中間結果
保持クエリ。 Wikipedia の各ページが表示された日時をまとめたテーブル (サンプル サイズは 10M) があり、date1 で表示されたページに対する date1 と date2 の両方でレビューされたページの割合を date1 と date2 ごとに検索する必要があるとします (date1 < date2)。
簡単な方法では、join 演算子と 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
出力
Day1 | Day2 | パーセント |
---|---|---|
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 |
上記のクエリの所要時間は約 18 秒でした。
hll()
、hll_merge()
、および dcount_hll()
の各関数を使用した場合は、同等のクエリが約 1.3 秒後に終了し、hll
関数が上記のクエリより約 14 倍速くなることが示されます。
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
出力
day1 | day2 | パーセント |
---|---|---|
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 |
Note
hll
関数のエラーが原因で、クエリの結果が 100% 正確ではありません。 エラーの詳細については、「dcount()
」を参照してください。