谷歌应用引擎:JDO +数据存储的性能不佳

时间:2010-04-17 05:38:31

标签: java performance google-cloud-datastore jdo

我有一个简单的数据模型,包括

用户:存储基本信息(密钥,姓名,电话等)

关系:描述,例如两个用户之间的友谊(提供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

6 个答案:

答案 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实体的数量。

相关问题