7

私はGoogleAppEngineを使用しており、低レベルのJavaAPIを使用してBigTableにアクセスしています。私は4つのレイヤーでSAASアプリケーションを構築しています:

  • クライアントWebブラウザ
  • RESTfulリソースレイヤー
  • ビジネスレイヤー
  • データアクセス層

私は自分のモバイルオートディテーリング会社(およびそのような他の会社)の管理に役立つアプリケーションを構築しています。私はこれらの4つの別々の概念を表現する必要がありますが、私の現在の計画が良いものであるかどうかはわかりません。

  • 予定
  • ラインアイテム
  • 請求書
  • 支払い

アポイントメント:「アポイントメント」とは、サービスを提供するために従業員が期待される場所と時間です。

広告申込情報:「広告申込情報」とは、サービス、料金、割引、およびそれに関連する情報です。予定に入る可能性のある広告申込情報の例:

名前:価格:手数料:時間の見積もり   
フルディテール、レギュラーサイズ:160753.5時間
フルディテールクーポンが$10オフ:-1000時間
プレミアムディテール:2201104.5時間
派生合計(ラインアイテムではない):$ 370 $1858.0時間

請求書:「請求書」は、顧客が支払うことを約束した1つ以上の広告申込情報の記録です。

支払い:「支払い」は、どのような支払いが行われたかを記録したものです。

このアプリケーションの以前の実装では、作業はより単純であり、これらの4つの概念すべてをSQLデータベースの1つのテーブル「予定」として扱いました。1つの「予定」には、複数の広告申込情報、複数の支払い、および1つの請求書を含めることができます。請求書は、広告申込情報と顧客レコードから作成された単なる電子メールまたは印刷物でした。

10回のうち9回、これは正常に機能しました。1人の顧客が1台または数台の車両を1回予約し、自分で支払いをしたとき、すべてが壮大でした。しかし、このシステムは多くの条件下で機能しませんでした。例えば:

  • 1人の顧客が1回の予約をしたが、途中で雨が降り、詳細担当者が翌日戻ってくる必要があった場合、2回の予約が必要でしたが、1つの広告申込情報、1つの請求書、1つの支払いのみでした。
  • オフィスの顧客グループ全員が割引を受けるために同じ日に車を運転することに決めたとき、私は1つの予約が必要でしたが、複数の請求書と複数の支払いが必要でした。
  • 1人の顧客が1回の小切手で2回の予約を支払った場合、2回の予約が必要でしたが、請求書と支払いは1回だけでした。

物事を少し混乱させることで、これらすべての外れ値を処理することができました。たとえば、詳細担当者が翌日に戻ってくる必要がある場合、2日目に「完了」という広告申込情報を使用して別の予定を立てると、費用は$0になります。または、1人の顧客が1回の小切手で2回の予約に対して支払いを行う場合、各予約に分割支払いレコードを入れます。これに伴う問題は、データの不一致の大きな機会を生み出すことです。データの不一致は、特に、顧客が1回の小切手で2回の予約を支払った、3番目の例などの財務情報が関係する場合に深刻な問題になる可能性があります。売掛金を適切に追跡するには、支払いを提供された商品やサービスと直接照合する必要があります。

提案された構造:

以下は、このデータを整理および保存するための正規化された構造です。おそらく私の経験不足のために、データの不一致エラーを回避するための優れた方法のように思われるため、データの正規化に重点を置いています。この構造により、他のテーブルの更新を気にすることなく、1回の操作でデータの変更を行うことができます。ただし、読み取りには、データのメモリ内編成と組み合わせた複数の読み取りが必要になる場合があります。後でわかりますが、パフォーマンスの問題がある場合は、「安全な」正規化された構造をそのままに保ちながら、クエリを高速化するために、いくつかの非正規化フィールドを「予定」に追加できます。非正規化により、書き込みが遅くなる可能性があります。

テーブル:

Appointment
 start_time
 etc...

Invoice
 due_date
 etc...

Payment
 invoice_Key_List
 amount_paid
 etc...

Line_Item
 appointment_Key_List
 invoice_Key
 name
 price
 etc...

以下は、特定の予定のリストに対して4つのエンティティ(テーブル)すべてを結び付けるために必要な一連のクエリと操作です。これには、各アポイントメントにスケジュールされたサービス、各アポイントメントの合計コスト、および各アポイントメントに対して受け取った未払いの天気に関する情報が含まれます。これは、予定のスケジューリングのためにカレンダーをロードするとき、またはマネージャーが操作の全体像を取得するときの一般的なクエリです。

  • 「start_time」フィールドが指定された範囲内にある「Appointments」のリストのQUERY。
    • 返された予定の各キーをリストに追加します。
  • アポイントメント_キー_リストフィールドのすべての「Line_Items」のクエリには、返品アポイントメントのいずれかが含まれます
    • すべてのラインアイテムの各invoice_keyをSetコレクションに追加します。
  • 請求書セット内のすべての「請求書」のクエリ(これは、App Engineを使用した1回の非同期操作で実行できます)
    • 返された請求書の各キーをリストに追加します
  • invoice_key_listフィールドにあるすべての「支払い」のQUERYには、返された請求書のいずれかに一致するキーが含まれています
  • 各予定が、予定されているline_items、合計価格、合計推定時間、および支払い済みかどうかを反映するように、メモリ内で再編成します。

...ご覧のとおり、この操作には4つのデータストアクエリとメモリ内の編成が必要です(メモリ内がかなり高速になることを願っています)

誰かがこのデザインについてコメントできますか?これは私が思いつくことができる最高のものですが、一般的に、または特にGAE(google app engine)の長所、短所、および機能の下で、より良いオプションまたは完全に異なるデザインがより適切に機能する可能性があると思います。 。

ありがとう!

使用法の明確化

ほとんどのアプリケーションは読み取りを多用し、一部のアプリケーションは書き込みを多用します。以下に、ユーザーが実行したい典型的なユースケースと内訳操作について説明します。

マネージャーは顧客から電話を受けます:

  • 読み取り-マネージャーはカレンダーをロードし、利用可能な時間を探します
  • 書き込み-マネージャーが顧客に情報を問い合わせます。これは、マネージャーが電話番号、名前、電子メール、住所などの各情報を入力するときの非同期読み取りの連続であると考えました。または、必要に応じて、おそらく1つクライアントアプリケーションがすべての情報を収集し、送信された後、最後に書き込みます。
  • 書き込み-マネージャーは顧客のクレジットカード情報を削除し、別の操作として顧客のレコードに追加します
  • 書き込み-マネージャーはクレジットカードに請求し、支払いが完了したことを確認します

マネージャーが電話をかけます。

  • 読み取りマネージャーがカレンダーをロードします
  • Read Managerは、電話をかけたい顧客の予定を読み込みます
  • 書き込みマネージャーが[呼び出し]ボタンをクリックすると、呼び出しが開始され、新しいCallReacordエンティティが書き込まれます
  • 読み取り呼び出しサーバーは呼び出し要求に応答し、CallRecordを読み取って呼び出しの処理方法を確認します
  • Write Callサーバーは、更新された情報をCallRecordに書き込みます
  • 呼び出しが閉じられたときに書き込み、呼び出しサーバーは、CallRecordリソースを更新するためにサーバーに別の要求を行います(注:この要求はタイムクリティカルではありません)

受け入れられた回答:: 上位2つの回答はどちらも非常に思慮深く、高く評価されました。私は、彼らの露出を可能な限り均等にするために、投票数の少ないものを受け入れました。

4

3 に答える 3

9

Webサイトが提供する必要のある2つの特定の「ビュー」を指定しました。

  1. 予定のスケジュール。あなたの現在のスキームはこれに対してうまく機能するはずです-あなたはあなたが言及した最初のクエリをする必要があるでしょう。

  2. オペレーションの全体像。これが何を意味するのかはよくわかりませんが、これを取得するために上記の4つのクエリの文字列を実行する必要がある場合は、設計にいくつかの改善を加えることができます。詳細は以下をご覧ください。

4つのデータストアクエリ自体は、必ずしも行き過ぎではありません。あなたの場合の問題は、2つのクエリが高価で、おそらく不可能でさえあるということです。各クエリを実行します。

  1. 予定のリストを取得する-問題ありません。このクエリは、インデックスをスキャンして、指定した日付範囲の予定を効率的に取得できるようになります。

  2. #1から各予定のすべての広告申込情報を取得します-これは問題です。このクエリでは、クエリを実行する必要がありINます。 クエリは舞台裏でサブクエリにIN変換されるため、#1の予定キーごとに1つのクエリが作成されます。これらは並行して実行されるため、それほど悪くはありません。主な問題は、クエリが少数の値のリスト(最大30個の値)に制限されていることです。#1から返された30を超える予定キーがある場合、このクエリは実行に失敗します。NIN

  3. ラインアイテムによって参照されるすべての請求書を取得します-問題ありません。関連するすべての請求書をキーで直接取得できるため、このクエリは安価です。(注:このクエリはまだ同期しています-非同期があなたが探していた言葉ではなかったと思います)。

  4. #3によって返されたすべての請求書のすべての支払いを取得します-これは問題です。#2と同様に、このクエリはクエリになり、IN#3が支払いを取得する必要のある適度な数の請求書を返す場合でも失敗します。

#1と#3によって返されるアイテムの数が十分に少ない場合、GAEはほぼ確実に許可された制限内でこれを行うことができます。そして、それはあなたの個人的なニーズには十分なはずです-それはあなたがほとんどそれを機能させるために必要であり、膨大な数のユーザーに拡張するためにそれを必要としないようです(そうではありません)。

改善のための提案:

  • 非正規化!特定の予定に関連する、、、およびエンティティのキ​​ーをLine_Item、予定自体のリストに保存してみてください。次に、クエリを削除できます。インデックスの爆発的な問題を回避するために、これらの新しいインデックスが作成されていないことを確認してくださいInvoicePaymentINListProperty

改善のための他のあまり具体的でないアイデア:

  • 「操作の全体像」が何を表示するかによっては、このすべての情報の取得を分割できる場合があります。たとえば、アポイントメントのリストを表示することから始めて、マネージャーが特定のアポイントメントに関する詳細情報を必要とする場合は、先に進んでそのアポイントメントに関連する情報を取得します。このインタラクションを単一のページで行う場合は、AJAXを介してこれを行うこともできます。
  • Memcacheは友だちです。Memcacheを使用してデータストアクエリの結果(またはさらに高レベルの結果)をキャッシュし、アクセスのたびに最初から再計算する必要がないようにします。
于 2010-06-25T18:48:14.717 に答える
7

お気づきのように、このデザインは拡張性がありません。ページをレンダリングするには、4つの(!!!)DBクエリが必要です。それは3つ多すぎます:)

App Engineデータストアを使用する一般的な概念は、何かが書き込まれるときにできるだけ多くの作業を行いたいということです。そのため、何かを取得してレンダリングするときにほとんど何もする必要はありません。おそらく、データがレンダリングされる回数と比較して、データを書き込む回数はごくわずかです。

正規化も同様に、あなたが努力しているように見えるものです。データストアは正規化に値を設定しません。データの不一致が少なくなる可能性がありますが、データの読み取りが非常に遅くなることも意味します(4回の読み取り?!!)。データは書き込まれるよりもはるかに頻繁に読み取られるため、データが複製されたり、短時間同期がずれたりすることがある場合でも、読み取り用に最適化してください。

データが保存されたときにどのように見えるかを考えるのではなく、ユーザーに表示されたときにデータがどのように見えるかを考えてください。事前にレンダリングされたHTMLをデータストアに文字通り保存することを意味する場合でも、できるだけその形式に近づけて保存します。読み取りは非常に高速になり、それは良いことです。

したがって、読み取りを最適化する必要があるため、多くの場合、書き込みは巨大な比率に成長します。非常に巨大なので、リクエストの30秒の制限時間に収まりません。それがタスクキューの目的です。モデルの「最低限の必需品」と見なすものをデータストアに保存し、タスクキューを起動してモデルを引き出し、レンダリングするHTMLを生成して、バックグラウンドで配置します。これは、タスクが完了するまでモデルをすぐに表示する準備ができていることを意味する場合があります。そのため、データが完全に入力されるまでモデルを「遅い方法」でレンダリングすることを意味する場合でも、この場合は適切な劣化が必要になります。それ以上の読み取りは非常に高速になります。

要約すると、データベースに直接関連する具体的なアドバイスはありません。これは、ユーザーがデータを表示したときにデータをどのように表示するかによって異なります。

私があなたに与えることができるのは、データストアに関するいくつかの非常に役立つビデオへのリンクです。

  • Brett Slatkinの2008年2009年は、App Engineでスケーラブルで複雑なアプリを構築することについて話し、今年はデータパイプラインについて素晴らしいものを紹介しています(これは直接適用できないと思いますが、一般的には本当に便利です)
  • 裏でのAppEngine:舞台裏でのAppEngineの機能
  • AppStats:実行しているデータストア読み取りの数を確認するための優れた方法と、その数を減らすためのヒント
于 2010-06-25T18:32:13.893 に答える
2

ここに、あなたが対処しなければならないと思ういくつかのアプリエンジン固有の要因があります:

于 2010-06-25T18:55:47.500 に答える