首页 文章

如何减少对数据存储区的请求数

提问于
浏览
0

根据AppStats,当运行以下200个文档和1个DocUser时,脚本大约需要5000毫秒 . 结果是,对于lastEditedBy(datastore_v3.Get)的每个锁定,每个锁定请求数据存储区为6-51ms .

我正在尝试做的是制作一些能够显示许多具有多个属性的实体,其中一些属性来自其他实体 . 永远不会有大量实体(<5000),因为这更像是一个管理界面,所以不会有很多同时用户 .

我尝试通过缓存DocUser实体进行优化,但是我无法从上面的查询中获取DocUser密钥,而无需向数据存储区发出新请求 .

1)这是否有意义 - 我遇到的延迟是否正常?

2)有没有办法在没有对数据存储区的额外请求的情况下完成这项工作?

models.py

class Document(db.Expando):
    title = db.StringProperty()
    lastEditedBy = db.ReferenceProperty(DocUser, collection_name = 'documentLastEditedBy')  
...

class DocUser(db.Model):
    user = db.UserProperty()
    name = db.StringProperty()  
    hasWriteAccess= db.BooleanProperty(default = False)
    isAdmin = db.BooleanProperty(default = False)
    accessGroups = db.ListProperty(db.Key)
...

main.py

$out = '<table>'   
documents = Document.all()
for i,d in enumerate(documents):        
    out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
$out = '</table>'

3 回答

  • 4

    一种方法是预取所有文档以生成查找字典,其中键为docuser.key(),值为docuser.name .

    docusers = Docuser.all().fetch(1000)
        docuser_dict = dict( [(i.key(), i.name) for i in docusers] )
    

    然后在您的代码中,您可以使用get_value_for_datastore从docuser_dict获取名称,以获取docuser.key(),而无需从数据存储中提取对象 .

    documents = Document.all().fetch(1000)
        for i,d in enumerate(documents):
            docuser_key = Document.lastEditedBy.get_value_for_datastore(d)
            last_editedby_name = docuser_dict.get(docuser_key)
            out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, last_editedby_name)
    
  • 1

    这是典型的反模式 . 您可以通过以下方式解决此问题

  • 1

    如果要剪切实例时间,可以将单个同步查询分解为多个异步查询,这可以在您执行其他工作时预取结果 . 而不是使用Document.all() . fetch(),而是使用Document.all() . run() . 您可能必须阻止您迭代的第一个查询,但是当它完成时,所有其他查询将完成加载结果 . 如果您想获得200个实体,请尝试一次使用5个查询 .

    q1 = Document.all().run(prefetch_size=20, batch_size=20, limit=20, offset=0)
    q2 = Document.all().run(prefetch_size=45, batch_size=45, limit=45, offset=20)
    q3 = Document.all().run(prefetch_size=45, batch_size=45, limit=45, offset=65)
    q4 = Document.all().run(prefetch_size=45, batch_size=45, limit=45, offset=110)
    q5 = Document.all().run(prefetch_size=45, batch_size=45, limit=45, offset=155)
    for i,d in enumerate(q1):        
        out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
    for i,d in enumerate(q2):        
        out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
    for i,d in enumerate(q3):        
        out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
    for i,d in enumerate(q4):        
        out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
    for i,d in enumerate(q5):        
        out += '<tr><td>%s</td><td>%s</td></tr>' % (d.title, d.lastEditedBy.name)
    

    我为我糟糕的蟒蛇道歉;但这个想法很简单 . 设置prefetch_size = batch_size = limit,并立即启动所有查询 . q1的大小较小,因为我们会先阻塞它,阻塞就是浪费时间 . 到q1完成时,q2将完成或几乎完成,而q3-5将支付零延迟 .

    有关详细信息,请参见https://developers.google.com/appengine/docs/python/datastore/async#Async_Queries .

相关问题