作者 在GAE的程序中实现分页功能的几个问题
Chris1919
2009-03-18 00:01
在实现YTBlog分页功能的时候,碰到几个datastore相关的问题。第一个问题,要做分页得知道所有Blog的数目,有几种方法:

1、创建一个Query或者GqlQuery对象,调用其count()方法。但这个方法有很大的局限性,首先限制最多只能返回1000,而且其执行时间随着结果的数目增加而增加,官方推荐只有在结果集很小的时候才使用。而这里要取Blog数目只能忍受常量时间,这个功能还会在首页用到,所以不能采纳。

2、 变通一下,先解决最大只能返回1000的问题,可以调用fetch()方法,如果返回的数目只有1000个,则继续递归调用,直至返回数目小于1000,把所有的数目加起来则是Blog的数目。

3、官方文档中讲Query是iterable的,那么就用Python中iterable的方法来取数目。不过估计这个方法直接执行的效率比不上方法2。

再说效率的问题,上面三种方案可以用memcache解决效率问题,把算出来的结果存在memcache中,当添加或者删除Blog的时候也主动更新memcache。但是代码实现起来就不是那么简单直接了,因为考虑到会碰到组合各种条件查询的情况,要对各种查询条件都维护一个memcache中的 Blog数目是很麻烦的,特别是添加或者删除Blog的时候。

4、自己维护一个Blog数目meta data存在datastore中。也可以解决数目和效率的问题。当然,也会碰到组合查询条件的问题,有个简单的解决方法么,就是限制分页的应用场景,也就是限制查询条件的组合。

有 了准确的Blog数目,页面上的分页就可以创建出来了。但第二个问题随之出现,假设每页显示10个Blog,需要直接跳至第101页,那么需要地用 Query的fetch()方法,其中limit=10,offset=1000。官方文档中又讲了,fetch方法的执行效率是随 limit+offset而线性增长的,而不仅仅是limit,也就是这里的复杂度得用1010来衡量。效率问题仍然存在。

上面两个问题在关系型数据库里应该也是一样存在的吧?不知道别人实现分页是怎么做的。

这里我对count()和fetch()的实现有些疑问。Google为什么会限制返回查询结果的数目为1000呢。实际上当用 limit=10,offset=1000的条件fetch的时候,datastore是返回1010个结果给fetch()的,只是fetch()把前 面1000条过滤掉了(参见这里关于fetch方法的说明)。这么设计的初衷是什么?

YTBlog里面具体的是这样实现的:只在界面上提供了"上一页"和"下一页"功能,这样就避开了第一个问题。实现代码在这里,BlogController的子类调用get_page_objects即可获得分页信息,调用代码在这里
Bookmark and Share
rolle.xu
2009-04-10 12:35
调用其count()方法。但这个方法有很大的局限性,首先限制最多只能返回1000,而且其执行时间随着结果的数目增加而增加,官方推荐只有在结果集很小的时候才使用。而这里要取Blog数目只能忍受常量时间,这个功能还会在首页用到,所以不能采纳。
---
不是这样的吧
好像文档上面说 fetch 最多返回1k,
没有说count啊

Chris1919
2009-04-10 16:56
我再查了一下,文档上也提到count了。参见这里:
Note: count() returns a maximum of 1000. If the actual number of entities that match the query criteria exceeds the maximum, count() returns a count of 1000.
rolle.xu
2009-04-12 10:54

我看到了
这个限制有点恶心

我看google那篇文章介绍分页,也是只提供上一页 下一页

http://code.google.com/appengine/articles/paging.html
Chris1919
2009-04-12 12:29
它这个分页实现有一点比较好,解决了上面提到的fetch的效率问题。不过稍微有点麻烦,不够通用。

GAE Datastore的scalability比较好。但与关系型数据库比也有很多软肋,目前应用场景相对有限吧,比较适合一些互联网开发。
rolle.xu
2009-04-13 11:17
呵呵,多谢
最近,对GAE很感兴趣,就学习学习。

看你已经做了很多东西,回头有空要多想你请教啊。

我是在google group GAE Shanghai 看到你的
你也在上海吗?

希望多认识些这方面的朋友 ^_^
Chris1919
2009-04-13 12:12
客气了,我也在上海,多交流 :)
rolle.xu
2009-04-13 13:50
它的方法 只是讲了next, prev其实做起来也有点麻烦呢。

我估计,datastore可以对排序列表进行快速定位,而这种没有排序的列表,就只能一个一个遍历了
所以效率很低。

看来分页这部分要重写了

不过,正常来讲 数据库不大,用你这个方法也够了。

另外 datastore还有个限制 好像是记录不能超过1M
看起来 数据也大不到哪去 呵呵
Chris1919
2009-04-13 16:19
GAE datastore所做的任何query操作必须有相应index存在,如果做一个没有index的query就会有异常,所以GAE中任何query肯定是快速的,当然带来一个坏的副作用就是数据更新比较慢。这篇文章就是根据这个原理来设计的分页,不用fetch,因为fetch的效率随offset+limit线性增长,而用query,保证读取每一页的时间是相等的(应该仍然跟总数据条目相关)。

GAE Python的文档讲过不能超过1M?我只看到GAE Java里有这个描述。
rolle.xu
2009-04-14 10:31
噢 我看错了
是一条记录不能超过 1 Mbyte
yuntian.ge
2010-02-11 15:33
GAE Datastore Query已经加入了cursor功能,详情请见这里。这个功能很好,不过也不能完美解决常量时间分页问题。