0

根据 AppStats,当使用 200 个文档和 1 个 DocUser 运行以下脚本时,脚本大约需要 5000 毫秒。罪魁祸首是 lastEditedBy (datastore_v3.Get) 的每次锁定都会向数据存储发出请求,每次锁定需要 6-51 毫秒。

我正在尝试做的是做一些事情,可以显示具有多个属性的许多实体,其中一些属性是从其他实体派生的。永远不会有大量实体(<5000),并且由于这更像是一个管理界面,因此永远不会有很多同时用户。

我试图通过缓存 DocUser 实体进行优化,但如果不对数据存储区发出新请求,我就无法从上面的查询中获取 DocUser 键。

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

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

模型.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)
...

主文件

$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>'
4

3 回答 3

4

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

于 2012-04-28T23:27:54.827 回答
1

一种方法是预取所有文档生成一个查找字典,键是 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)
于 2012-04-29T04:40:43.260 回答
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

于 2012-04-29T20:07:54.173 回答