我有一个简单的数据模型,包括
用户:存储基本信息(密钥,姓名,电话等)
关系:描述,例如两个用户之间的友谊(提供relationship_type +两个用户密钥)
评论:由用户发布(密钥,评论文本,user_id)
我的表现非常糟糕,例如,如果我尝试打印所有用户朋友的名字。假设用户有500个朋友:我可以在一个查询中非常轻松地获取朋友user_ids的列表。但是,为了取出名字,我必须做500次来回数据存储,每次看起来大约需要30毫秒。如果这是SQL,我只是做一个JOIN并快速得到答案。
据我所知,在JDO的轻松实施中,有一些基本的连接方式可用于跨越非独立关系(如http://gae-java-persistence.blogspot.com所述),但它们听起来是实验性的和非标准的(例如我的代码赢得了'在任何其他JDO实现中工作)。
更糟糕的是,如果我想提取用户朋友发布的所有评论,该怎么办?然后我需要从用户 - >关系 - >评论,即三向连接,甚至不是通过实验支持的。获得朋友列表500次来回的开销+另外500次访问以查看用户的朋友是否有任何评论已足以推动运行时间> 30秒。
人们如何在实际数据存储支持的JDO应用程序中处理这些问题? (或他们呢?)
在这种(非常常见的)情况下,有没有人设法从JDO / Datastore中提取出令人满意的表现?
-Bosh
答案 0 :(得分:3)
首先,对于经常访问的对象(如用户),我依赖于memcache。这应该会加速你的应用程序。
如果您必须转到数据存储区,正确的方法应该是getObjectsById()
。不幸的是,它看起来像GAE doesn't optimize this call。但是,contains()
对密钥的查询是optimized,以便在一次访问数据存储区时获取所有对象,这就是您应该使用的内容:
List myFriendKeys = fetchFriendKeys();
Query query = pm.newQuery(User.class, ":p.contains(key)");
query.execute(myFriendKeys);
您还可以依赖接受多个密钥的低级API get()
,或者像我一样使用objectify。
完全不同的方法是在list属性上使用相等过滤器。如果列表中的任何项匹配,则匹配。因此,如果您的用户实体中有friendOf
列表属性,则可以发出单个查询friendOf == theUser
。您可能需要查看此内容:http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine
答案 1 :(得分:2)
您 以最小化数据库读取。对于任何GAE项目来说,这一定是一个巨大的焦点 - 其他任何东西都会花费你。要做到这一点,尽可能多地预先计算,尤其是读取信息。要解决阅读500个朋友姓名的问题,请考虑您可能更改朋友列表的次数远远少于阅读它,因此在每次更改时,将所有名称存储在您可以一次阅读的结构中。
如果你绝对不能,那么你必须手动调整每个案例,例如:使用低级API进行批量获取。
另外,优化速度而不是数据大小。使用额外的结构作为索引,以多种方式保存对象,以便您可以尽快读取它。数据便宜,CPU时间不是。
答案 2 :(得分:1)
不幸的是Phillipe的建议
Query query = pm.newQuery(User.class, ":p.contains(key)");
仅针对按主键搜索时进行单个查询进行了优化。例如,传入十个非主键值的列表会给出以下跟踪 alt text http://img293.imageshack.us/img293/7227/slowquery.png
我希望能够从所有用户的朋友那里批量获取评论。如果我在每个用户上存储一个List,则该列表的长度不能超过1000个元素(如果它是用户的索引属性),如:http://code.google.com/appengine/docs/java/datastore/overview.html所述。
似乎越来越像我在这里使用了错误的工具集。
-B
答案 3 :(得分:0)
Facebook拥有28TB的内存缓存......然而,500次访问memcached也不是很便宜。它不能用于存放大量的小件物品。 “反异化”是关键。此类应用程序不需要支持即席查询。直接计算并存储结果,以支持少数支持的查询。
在您的情况下,您可能只有一种类型的查询 - 返回此数据,以及应在用户页面上显示的其他数据。你可以预先计算这个混乱的大球,所以后来一个基于userId的查询可以全部获取。
当userA对userB发表评论时,你会检索到userB的混乱局面,在其中插入userA的评论并保存。
当然,这种方法存在很多问题。对于巨型互联网公司,他们可能没有选择,通用查询引擎只是不削减它。但对其他人?如果你能使用好的旧RDBMS,你会不会更高兴?
答案 4 :(得分:0)
如果它是经常使用的查询,您可以考虑为相同的索引准备索引。 http://code.google.com/appengine/articles/index_building.html
答案 5 :(得分:0)
索引属性限制现在提高到5000
但是,使用http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine中描述的方法,您可以比这更高
基本上只有一堆用户称为UserFriends的子实体,因此拆分大列表并将限制提高到n * 5000,其中n是UserFriends实体的数量。