Google Appengine 数据存储的层次结构优化

Posted

技术标签:

【中文标题】Google Appengine 数据存储的层次结构优化【英文标题】:Hierarchy Optimization on Google Appengine Datastore 【发布时间】:2009-06-21 04:55:23 【问题描述】:

我使用如下模型将分层数据存储在数据存储区中:

class ToolCategories(db.Model):  
   name = db.StringProperty()  
   parentKey = db.SelfReferenceProperty(collection_name="parent_category")  
   ...  
   ...  

我想打印所有保留层次结构的类别名称,例如:

--Information Gathering  
----OS Fingerprinting  
----DNS  
------dnstool  
----Port Scanning   
------windows  
--------nmap  
----DNS3  
----wireless sniffers  
------Windows  
--------Kismet  

为了完成上述操作,我使用了使用反向引用功能的简单递归:

class GetAllCategories (webapp.RequestHandler) :


        def RecurseList(self, object, breaks) :
                output = breaks + object.name + "</br>"
                for cat in object.parent_category:
                        output = output + self.RecurseList(cat, breaks + "--")

                return output



        def get (self) :
                output = ""
                allCategories = ToolCategories.all().filter(' parentKey = ', None)
                for category in allCategories :
                        output = output + self.RecurseList(category, "--")

                self.response.out.write(output)

由于我对 App 引擎编程非常陌生(距离我开始编写代码还不到 3 天),我不确定从数据存储访问的角度来看,这是否是完成所需工作的最优化方式。

这是最好的方法吗?如果不是,那是什么?

【问题讨论】:

【参考方案1】:

您的方法的主要缺点是,由于您使用的是“邻接表”表示树的方式,因此您必须对树的每个分支进行一次数据存储查询。数据存储查询相当昂贵(每个大约 160 毫秒),因此构建树,特别是如果它很大,可能会相当昂贵)。

还有另一种方法,本质上是数据存储区用来表示实体组的方法:不只是存储父键,而是使用 ListProperty 存储整个祖先列表:

class ToolCategories(db.Model):
  name = db.StringProperty()
  parents = db.ListProperty(db.Key)

然后,要构建树,您可以在一个查询中检索整个事物:

q = ToolCategories.all().filter('parents =', root_key)

【讨论】:

尼克,感谢您的指点!作为 Appengine 新手,我面临的问题是我无法可视化我编写的任何 Datastore 访问语句的实际 Datastore 查询数量。使用 SQL 很容易做到这一点,因此可以估计查询的“成本”。我在任何地方都找不到关于“数据存储查询成本”的好文档,因此陷入了优化问题。这方面有详细的文件吗? 所有数据存储查询的成本与返回的条目数成正比,往返数据存储的(大)常数因子。因此,您通常只需将往返次数和返回的实体数相加,然后尝试优化两者。在您的原始示例中,当您检索 ReferenceProperty 的集合时执行的隐式数据存储操作会加剧该问题。 顺便说一下,我认为这是数据存储的优势之一:虽然 SQL 查询的成本从 SELECT 语句中并不明显,并且取决于数据的性质即使是单个数据库,datstore 查询也始终具有相同的成本,无论这些变量如何。【参考方案2】:

你有一个非常合理的方法!我的主要警告是与 GAE 关系不大,而与 Python 关系很大:不要++= 构建字符串。相反,您制作一个字符串片段列表(使用appendextend 或列表推导式&c),当您完成后,您使用''.join(thelist) 等将其加入最终字符串结果。尽管最近的 Python 版本努力优化 ++= 循环的内在 O(N squared) 性能,但最后你总是最好在此过程中建立字符串列表并 ''.joining 他们在最后!

【讨论】:

@Jake,感谢您的快速接受!不过,在没有赞成票的情况下获得接受很有趣,我认为这是我 2 个月以来第一次在 SO 上发生这种情况;-)。 感谢亚历克斯的建议!我将进行更改并在最终列表中使用 join()。只是需要快速澄清一下:从数据存储的角度来看,使用引用属性访问相关数据是最快的方法 - 我说的对吗? 我的浏览器在我投赞成票之前就崩溃了 :( ...现在都完成了 :) 感谢您的快速回复! 是的@Jake,你是对的——参考属性几乎是你为此目的所能做的最好的。 很好的评论,但这里字符串连接的低效率在进行 N 个数据存储查询的低效率之前显得相形见绌。 :)

以上是关于Google Appengine 数据存储的层次结构优化的主要内容,如果未能解决你的问题,请参考以下文章

是否可以为 appengine 数据存储实体获取 Google 电子表格的数据源 URL?

如何在没有AppEngine for Google云端存储的情况下获取服务网址?

使用 Google AppEngine 创建 Java Web 服务

Google AppEngine (GAE) - 完整的对象键

AppEngine数据存储 - 以编程方式备份

如何在 Google AppEngine 上使用 JDBC