Twitterのようなソーシャルサイト用のAppEngineデータストアをどのように設計しますか?
-
06-07-2019 - |
質問
メンバーがアクティビティを作成し、Google AppEngineを使用して他のメンバーのアクティビティをフォローするソーシャルアプリケーションを設計する最良の方法は何でしょうか。
より具体的には、これらのエンティティがあると仮定します:
- 友達がいるユーザー ユーザーによって行われたアクションを表す
- アクティビティ(それぞれが所有者ユーザーに文字列メッセージとReferencePropertyを持っている、またはappengineのキーを介して親の関連付けを使用できると言います)
難しいのは、友達のアクティビティを追跡することです。つまり、すべての友達の最新のアクティビティを集約することです。 通常、それはアクティビティテーブルとフレンドリスト間の結合ですが、それをシミュレートする結合がないため、appengineで実行可能な設計ではないため、N個のクエリ(Nは友人の数)を起動し、メモリにマージする必要があります-非常に高価で、おそらくリクエストの期限を超えます...)
現在、新しいアクティビティを作成するとバックグラウンドプロセスが起動され、新しいアクティビティのキーが「受信トレイ」に入れられる受信トレイキューを使用してこれを実装することを考えています。すべての次のユーザーの:
- Xをフォローするすべてのユーザーを取得する"可能なappengineクエリです
- 新しい" Inbox"への非常に高価なバッチインプットではありません。基本的に(ユーザー、アクティビティキー)タプルを格納するエンティティ。
このデザインや代替案などについての考えを聞いてうれしいです。
解決
App Engineでスケーラブルで複雑なアプリを構築( pdf )、a Google I / OでBrett Slatkinが行った興味深い講演。彼はTwitterのようなスケーラブルなメッセージングサービスを構築する問題に取り組んでいます。
リストプロパティを使用した彼のソリューションは次のとおりです。
class Message(db.Model):
sender = db.StringProperty()
body = db.TextProperty()
class MessageIndex(db.Model):
#parent = a message
receivers = db.StringListProperty()
indexes = MessageIndex.all(keys_only = True).filter('receivers = ', user_id)
keys = [k.parent() for k in indexes)
messages = db.get(keys)
このキーのみのクエリは、受信者のリストを逆シリアル化およびシリアル化することなく、指定した受信者と等しい受信者を持つメッセージインデックスを検索します。次に、これらのインデックスを使用して、必要なメッセージのみを取得します。
これを行うには、間違った方法があります:
class Message(db.Model):
sender = db.StringProperty()
receivers = db.StringListProperty()
body = db.TextProperty()
messages = Message.all().filter('receivers =', user_id)
クエリは、クエリによって返されたすべての結果を展開する必要があるため、非効率的です。したがって、各受信者リストに1,000人のユーザーがいる100のメッセージを返した場合、100,000(100 x 1000)のリストプロパティ値を逆シリアル化する必要があります。データストアの待機時間とCPUコストが高すぎます。
最初はこのすべてにかなり混乱していたので、リストプロパティの使用に関する短いチュートリアル。お楽しみください:)
他のヒント
ソーシャルアプリケーションに最適なデザインであるかどうかはわかりませんが、 jaiku は App Engineに移植されました会社がGoogleに買収されたときの元の作成者によるものであるため、合理的である必要があります。
ロバート、提案されたソリューションについて:
messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])
ndb.TextProperty" body"ははインデックス付けされていないため、プロジェクションでは使用できません。プロジェクションはインデックス付きプロパティのみをサポートします。 有効な解決策は、MessageとMessageIndexの2つのテーブルを維持することです。
NDBの新しいProjection Queriesでこれを解決できると思います。
class Message(ndb.Model):
sender = ndb.StringProperty()
receivers = ndb.StringProperty(repeated=True)
body = ndb.TextProperty()
messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])
これで、リストプロパティの逆シリアル化にかかる費用を処理する必要がなくなりました。