首页 文章

您将如何为Twitter等社交网站设计AppEngine数据存储?

提问于
浏览
28

我想知道什么是设计社交应用程序的最佳方式,其中成员使用Google AppEngine进行活动并关注其他成员的活动 .

更具体一点,假设我们有这些实体:

  • Users 谁有朋友

  • Activities 表示用户所做的操作(假设每个用户都有一个字符串消息和一个ReferenceProperty给它的所有者用户,或者它可以通过appengine的密钥使用父关联)

困难的部分是关注您朋友的活动,这意味着汇总您所有朋友的最新活动 . 通常情况下,这将是活动表和您的朋友列表之间的连接,但这不是一个可行的appengine设计,因为没有连接模拟它将需要启动N个查询(其中N是朋友的数量)然后合并到内存中 - 非常昂贵,可能超过要求截止日期......)

我目前正在考虑使用收件箱队列来实现这一点,其中创建新的Activity将触发后台进程,该进程将新活动的密钥放在每个后续用户的“收件箱”中:

  • 获取"All the users who follow X"是一个可能的appengine查询

  • 对于基本上存储(用户,活动密钥)元组的新"Inbox"实体而言,不是非常昂贵的批量输入 .

我很高兴听到这个设计或其他建议等的想法 .

4 回答

  • 7

    看看Building Scalable, Complex Apps on App Enginepdf),这是Brett Slatkin在Google I / O上发表的精彩演讲 . 他解决了构建像Twitter这样的可扩展消息传递服务的问题 .

    这是他的 solution 使用list属性:

    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)
    

    此仅密钥查询查找消息索引,其接收方等于您指定的接收方,而不反序列化和序列化接收方列表 . 然后使用这些索引只捕获所需的消息 .

    这是 the wrong way 这样做:

    class Message(db.Model):
        sender = db.StringProperty()
        receivers = db.StringListProperty()
        body = db.TextProperty()
    
    messages = Message.all().filter('receivers =', user_id)
    

    这是低效的,因为查询必须解包查询返回的所有结果 . 因此,如果您在每个接收者列表中返回100条消息,其中包含1,000个用户,则必须反序列化100,000(100 x 1000)个列表属性值 . 数据存储延迟和CPU的方式太贵了 .

    起初我对所有这些感到非常困惑,所以我写了一个short tutorial about using the list property . 请享用 :)

  • 0

    我认为现在可以通过NDB中的新投影查询来解决这个问题 .

    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])
    

    现在您不必处理反序列化列表属性的昂贵成本 .

  • 0

    我不知道它是否是社交应用程序的最佳设计,但当它被谷歌收购时,jaiku是它的原创者,所以它应该是合理的 .

    参见演员和老虎和熊,哦,我的!在design_funument.txt . 实体在common/models.py中定义,查询在common/api.py中 .

  • 24

    Robert,关于你提出的解决方案:

    messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])
    

    我认为ndb.TextProperty“body”不能与投影一起使用,因为它没有编入索引 . 预测仅支持索引属性 . 有效的解决方案是维护2个表:Message和MessageIndex .

相关问题