2

パフォーマンス上の理由から、GAE アプリケーションでシャード カウンター ( https://cloud.google.com/appengine/articles/sharding_counters ) を使用していますが、なぜこんなに遅いのか、どうすれば高速化できるのかを理解するのに苦労しています。 .

背景
一度に 20 個のオブジェクトのセットを取得する API があり、オブジェクトごとにカウンターから合計を取得して応答に含めます。

メトリクス
Appstats をオンにしてキャッシュをクリアすると、20 個のカウンターの合計を取得すると、datastore_v3.Get によって 120 個の RPC が作成され、2500 ミリ秒かかることがわかりました。

考察
これはかなりの数の RPC 呼び出しのようで、わずか 20 個のカウンターを読み取るのにかなりの時間がかかります。私はこれがより速いと思っていましたが、おそらくそれは私が間違っているところです。これよりも速いはずですか?

さらに調べ
て、 get_count メソッドの次の 2 行を見て、統計をもう少し掘り下げました。

all_keys = GeneralCounterShardConfig.all_keys(name)
for counter in ndb.get_multi(all_keys):

get_multi 行をコメントアウトすると、datastore_v3.Get による RPC 呼び出しが 20 回あり、合計で 185 ミリ秒かかることがわかります。

予想どおり、これにより、datastore_v3 による 100 回の RPC 呼び出しの原因は get_multi のままになります。2500 ミリ秒以上かかります。これは確認しましたが、ここで混乱しています。20 個のキーで get_multi を呼び出すと、100 回の RPC 呼び出しが発生するのはなぜですか?

更新 #1
GAE コンソールで Traces をチェックアウトしたところ、いくつかの追加情報が表示されました。彼らはそこにもRPC呼び出しの内訳を示していますが、そのサイトでは、「取得をバッチ処理してリモートプロシージャ呼び出しの数を減らす」と言っています。get_multi を使用する以外でそれを行う方法がわかりません。それが仕事だと思った。ここで何かアドバイスはありますか?

更新 #2
これは、私が見ている統計を示すスクリーン ショットです。最初のものは私のベースラインです - カウンター操作のない関数です。2 つ目は、1 つのカウンターのみに対する get_count の呼び出し後です。これは、6 つの datastore_v3.Get RPC の違いを示しています。

ベースライン ここに画像の説明を入力

1 つのカウンターで get_count を呼び出した後 ここに画像の説明を入力

更新 #3
Patrick の要求に基づいて、コンソールの Trace ツールからの情報のスクリーンショットを追加します。 ここに画像の説明を入力

4

2 に答える 2

1

各項目と実際の get_multi 呼び出し自体を通過する for ループを分割してみてください。次のようなものです:

all_values = ndb.get_multi(all_keys)
for counter in all_values:
    # Insert amazeballs codes here

私はそれが次のいずれかだと感じています:

  1. ジェネレーター パターン (for ループからの生成) が get_multi 実行パスで何かおかしなことを引き起こしている
  2. おそらく、期待している項目の数が実際の結果の数と一致しないため、GeneralCounterShardConfig.all_keys(name) の問題が明らかになる可能性があります
  3. シャード数の設定が高すぎます。シャードが 10 個を超えるとパフォーマンスの問題が発生することに気付きました。
于 2016-01-07T14:49:33.213 に答える
0

私が同様の問題を掘り下げたときに学んだことの 1 つはget_multi、アプリケーションから複数の RPC が送信される可能性があるということです。SDK のデフォルトは 1 回の取得で 1000 キーに設定されているようですが、実稼働アプリで観察したバッチ サイズははるかに小さく、10 程度です (メモリから)。

これが行われる理由は、バッチ サイズによっては、実際には複数の RPC を使用する方がよいからだと思います。アプリの RPC オーバーヘッドは増えますが、Datastore の並列処理は増えます。言い換えれば、これはおそらく多くのデータストア オブジェクトを読み取るための最良の方法です。

ただし、絶対的な最新の値を読み取る必要がない場合は、オプションを設定してみてください。ただし、それは古いライブラリdb.EVENTUAL_CONSISTENCYでのみ使用でき、 . (ただし、 Cloud Datastore API経由でも利用できるようです)。dbndb

詳細

App Engine SDK の Python コード、具体的にはファイルgoogle/appengine/datastore/datastore_rpc.pyを見ると、次の行が表示されます。

max_count = (Configuration.max_get_keys(config, self.__config) or
             self.MAX_GET_KEYS)
...

if is_read_current and txn is None:
  max_egs_per_rpc = self.__get_max_entity_groups_per_rpc(config)
else:
  max_egs_per_rpc = None

...

pbsgen = self._generate_pb_lists(indexed_keys_by_entity_group,
                                 base_req.ByteSize(), max_count,
                                 max_egs_per_rpc, config)

rpcs = []
for pbs, indexes in pbsgen:
  rpcs.append(make_get_call(base_req, pbs,
                            self.__create_result_index_pairs(indexes)))

これについての私の理解:

  • 設定max_countオブジェクトから、または1000デフォルトとして設定
  • リクエストが現在の値を読み取る必要がある場合はmax_gcs_per_rpc、構成から設定するか10、デフォルトとして設定します
  • max_countと の両方を制限として使用して、入力キーを個々の RPC に分割しmax_gcs_per_rpcます。

したがって、これは Python Datastore ライブラリによって行われています。

于 2016-12-12T18:11:50.173 に答える