29

私は基本的に古典的な多対多のモデルを持っています。ユーザー、アワード、およびユーザーとアワード間の「多対多」テーブルマッピング。

各ユーザーには約400の賞があり、各賞は約1/2のユーザーに与えられます。

ユーザーのすべてのアワードを繰り返して、ポイントを合計したいと思います。SQLでは、多対多間のテーブル結合になり、各行をウォークスルーします。MySQLインスタンスを備えたまともなマシンでは、400行はまったく大したことではないはずです。

App Engineでは、合計を行うのに約10秒かかります。ほとんどの時間はGoogleのデータストアで費やされています。これがcProfileの最初の数行です

   ncalls tottime percall cumtime percallファイル名:lineno(関数)
      462 6.291 0.014 6.868 0.015 {google3.apphosting.runtime._apphosting_runtime ___ python__apiproxy.Wait}
      913 0.148 0.000 1.437 0.002 datastore.py:524(_FromPb)
     8212 0.130 0.000 0.502 0.000 datastore_types.py:1345(FromPropertyPb)
      462 0.120 0.000 0.458 0.001 {google3.net.proto._net_proto ___ parse__python.MergeFromString}

私のデータモデルは間違っていますか?ルックアップを間違って行っていますか?これは、キャッシングとバルク更新に対処しなければならない欠点ですか(これはお尻の王室の痛みになります)。

4

5 に答える 5

20

両方のビットかもしれません;-)

Awards テーブルで 400 のクエリを実行している場合、マッピング テーブルのクエリで返される結果ごとに 1 つのクエリが実行されるとしたら、それは大変なことだと思います。クエリに 1,000 件の結果という制限があるのは、BigTable が 1,000 件の結果を返すことは、妥当な時間内に動作する能力の限界に達していると考えているためです。アーキテクチャに基づいて、400 のクエリは 400 の結果を返す 1 つのクエリよりもかなり遅くなると思います (400 log N 対 (log M) + 400)。

良いニュースは、GAE では、すべての賞とそのポイント値を含む単一のハッシュテーブルを memcaching するのは非常に簡単だということです (まあ、少し前に memcache のドキュメントに目を向けたときは非常に簡単に見えました。私はそれを行う必要はありませんでした)。まだ)。

また、まだご存じない場合は、for result in query.fetch(1000)は よりもはるかに高速でありfor result in query、どちらの方法でも 1000 件の結果に制限されています。後者の利点は、(1) 早期に救済すれば速くなる可能性があること、(2) Google が制限を 1000 を超えて引き上げた場合、コードを変更せずに利益を得られることです。

ユーザー (または賞) を削除するときにも問題が発生する可能性があります。あるテストで、制限時間内に 300 個のオブジェクトを削除できることがわかりました。これらのオブジェクトはマッピング オブジェクトよりも複雑で、3 つのプロパティと 5 つのインデックス (暗黙的なものを含む) を持ちますが、マッピング テーブルにはおそらく 2 つのプロパティと 2 つの (暗黙的な) インデックスしかありません。[編集: db.delete() がリストを取得できることを知る前に、このテストを行ったことに気付きました。これはおそらくはるかに高速です]。

BigTable は、リレーショナル データベースがうまく機能するように設計されていることを必ずしも実行するとは限りません。代わりに、多くのノードにデータを適切に分散します。しかし、ほぼすべての Web サイトは、単一の db サーバーでボトルネックが発生しても問題なく動作するため、BigTable が行うことを厳密に必要とするわけではありません。

もう 1 つ: 1 つの HTTP リクエストで 400 のデータストア クエリを実行している場合、リクエストの固定クォータに到達する前に、データストアの固定クォータに達していることがわかります。もちろん、クォータの範囲内に収まっている場合、または最初に何か他のことを実行している場合、これはアプリにとって無関係である可能性があります。しかし、2 つのクォータの比率は 8:1 程度です。これは、Google が私のデータ モデルをどのように期待しているのかを示すヒントとなります。

于 2009-06-05T08:44:42.140 に答える
19

私のデータモデルは間違っていますか? 私はルックアップを間違っていますか?

はい、はい、恐れ入ります。

データ モデルに関する限り、これを処理する最善の方法は、ユーザー レコードに対して合計を保存し、ユーザーが賞を獲得または喪失したときにそれを更新することです。ほとんどの場合スコアが変わらない場合、毎回スコアを数えても意味がありません。「UserAward」エンティティ タイプを「User」の子エンティティにすると、単一のアトミック トランザクションでスコアを更新し、UserAward エントリを挿入または削除できるため、カウントが常に正確になります。

onebyone さんは、賞品のテーブルを memcache できると指摘しています。それは良い考えですが、データの量が限られていることを考えると、ローカル メモリに格納する方がさらに良い方法です。グローバル メンバーは HTTP リクエスト間で保持されます。賞のテーブルを頻繁に更新することはないと思われるため、キャッシュの無効化についてあまり心配する必要はありません。最初のリクエストでロードするだけです (または、ソースにハードコードすることもできます)。アワードのリストを変更した場合、新しいマイナー アップデートをデプロイすると、すべてのインスタンスがリセットされ、リロードが発生します。

ルックアップの場合、データストア操作の実質的なコストは往復時間であることに注意してください。ID で 1 つ以上のレコードを検索する get() 操作 (バッチ処理が可能です!) には、約 20 ~ 40 ミリ秒かかります。ただし、クエリには約 160 ~ 200 ミリ秒かかります。したがって、非正規化の力。

于 2009-06-05T16:24:12.467 に答える
2

アプリ エンジンの重要なイディオムの 1 つは、ストレージは安価ですが、時間は決して余剰にならないということです。アプリ エンジンで多対多の関係を構築する最善の方法は、単純に両側に情報を格納することです。つまり、ユーザーには賞のリストがあり、各賞にはユーザーのリストがあります。ユーザーが持っているすべての賞を検索するには、特定のユーザーの賞テーブルをクエリするだけです。

このアイデアは、ここでよく示されています:スケーラブルで複雑なアプリの構築

于 2010-10-13T14:22:23.410 に答える
0

Google BigTable は、Google 分散ファイル システムで実行されます。

データが配布されます。おそらく 400 行の mysql の方が優れているかもしれませんが、データが大きい場合は google BigTable の方が高速になる可能性があります。

それが、memcache を使用して高速化することを奨励している理由だと思います。

于 2009-06-05T08:44:10.470 に答える