集計値の保存と計算


96

集計値を保存するタイミングと、その場でそれらを計算するタイミングを決定するためのガイドラインまたは経験則はありますか?

たとえば、ユーザーが評価できるウィジェットがあるとします(下のスキーマを参照)。ウィジェットを表示するたびに、Ratingsテーブルから平均ユーザー評価を計算できました。または、Widgetテーブルに平均評価を保存できます。これにより、ウィジェットを表示するたびに評価を計算する必要がなくなりますが、ユーザーがウィジェットを評価するたびに平均評価を再計算する必要があります。

Ratings       Widgets
---------     -------
widget_id     widget_id
user_id       name              
rating        avg_rating  <--- The column in question

回答:


58

場合によります。集計値を事前に計算すると、書き込みに大きな負荷がかかり、それらを導出すると読み取りが難しくなります

派生値に頻繁にアクセスする場合、事前計算は有効な非正規化ステップです。ただし、この場合は、マテリアライズドビュー(ディスクに書き込まれ、トリガーによって親テーブルにリンクされたビュー)を使用することをお勧めします。マテリアライズドビューは、頻繁に要求されるが退屈なデータを格納するように設計されており、多数の書き込みと少数の読み取りに役立ちます。

高書き込み、高読み取りのシナリオでは、マテリアライズドビューの効果を模倣するタスクをバックグラウンドで持つことを検討しますが、リアルタイムではありません。これにより、書き込みと読み取りのパフォーマンスを維持しながら、「十分に良い」平均が得られます。

どのような状況でも、派生列を「通常」列のように扱うべきではありません。ウィジェットの「ビュー」に表示されるデータがテーブルのどこかに存在することを確認してください。また、この質問はデータベース(およびデータベースバージョン)固有のものであるため、通常サイズのデータ​​セットとマテリアライズドビューに対する(適切なインデックスを使用した)集計のパフォーマンステストをお勧めします。


この議論は、マテリアライズドビューに関して非常に役立つことがわかりましたOracle向けに調整されていますが、一般的に理解できます。MySQLのバックグラウンドから来た私のような人にとって、MySQLビューはマテリアライズドビューとは異なり、仮想であり、ディスクに保存しません(リンクで説明したように)。
シッダールタ

賛成!正確な質問をしようとしていました、私はSMA、EMA、WMA、RSIなどのインジケータを保存する必要があり、重い計算が含まれます新しいデータが入ってきて、それらを維持するための良い戦略です。誰もが左右にビューのクエリを開始すると、ビューがデータベースを完全に引き裂くことがわかります
PirateApp

11

基礎となる数値が変更/更新される頻度と比較して、値を計算/表示する必要がある頻度。

したがって、1時間に1回しか変更されない値を表示する1日1万件のヒットがあるWebサイトがある場合、基になる値が変更されたときに計算します(データベーストリガーなど)。

2秒ごとに統計情報が変化する統計情報を表示するツールがあるが、アクセスできるのは3人だけで、1日に2、3回しか見ない場合は、計算する可能性が高くなりますその場で。(ただし、そもそも古いデータがあったことは大したことではないと計算するのに数分かかります...そして、上司はcronから1時間ごとに物を生成するように私に言うので、彼は持っていません彼がそれを見たい時を待つために。)


15分ごと、メトリックごとに1000行で100%変化する10個のメトリック
PirateApp

1
@PirateAppと、平均15分間に何回表示されますか?何もできることは何度もリロードを打つ続ける人々のためにそれをキャッシュその後、15分ウィンドウで最初の要求でそれを生成している
ジョー・

それはウェブサイト上にあるので、少なくとも10000人が初心者向けに見ていると
思い

1
問題は、変更の頻度に対する要求の数です。したがって、基礎となるデータが変更される前に10,000回表示されるものを事前に生成する場合は、はい、事前に生成します。1回しか表示されない場合、または1回未満しか表示されない場合(データが急激に変化するため、またはページがめったに表示されないため)、表示されません。
ジョー

4

StaleWidgetsテーブルを「無効な」(再計算される)ウィジェットのキューとして使用します。これらの値を再計算できる他のスレッド(非同期)タスクを使用します。再計算の期間または瞬間は、システム要件によって異なります。

  • ただ読んで、
  • 月末に、
  • 一日の始めに何人かのユーザーのために
  • ...

1
それらはどのようにして古いキューに入りますか?
jcolebrand

2
@jcolebrand ..一部のウィジェットの評価(評価表)を挿入/削除する瞬間。現時点では、Widgetsテーブルの平均値は無効になっているため、widget_idという1つの列のみを持つStaleWidgetsレコードをテーブルに挿入する必要があります。評価テーブルまたはもちろんあなたのバリアントにレコードを挿入するトリガーまたはストアドプロシージャを使用します。
ガリック

2

計算が煩雑でなく、複雑な計算と頻繁な更新があるが、計算されたデータを保存し、再計算が必要かどうかを保存する追加の列(bool)を持っているよりも頻繁ではない場合は、オンザフライで計算することをお勧めします。たとえば、再計算を行う必要があるが再計算を行わない場合は常にこの列をtrueに設定し、再計算を行う場合はこの列をfalseに設定します(これは計算値が最新で古くなっていないことを表します)。

この方法では、毎回再計算する必要はありません。列の値がtrueで、再計算する必要がある場合にのみ計算します。これにより、再計算の多くを節約できます。


2

特にケースについては、すべての評価を追加し、それを合計で割って平均を求める必要のない異なるソリューションがあります。代わりに、レビューの合計を含む他のフィールドを持つことができます。したがって、評価を追加するたびに(avg_rating×total + new_rating)/ totalを使用して新しい平均を計算します。すべての評価値にアクセスする必要はありません。同様のソリューションが他のケースに適用される場合があります。

これのマイナス面は、それが酸性取引ではないということです。そのため、古い評価で終わる可能性があります。しかし、それでもデータベースでトリガーを使用することでそれを解決できます。もう1つの問題は、データベースがもはや正規化されていないことですが、パフォーマンスと引き換えにデータを非正規化することを恐れないことです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.