| 作者 | 在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即可获得分页信息,调用代码在这里。 |
|
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功能,详情请见这里。这个功能很好,不过也不能完美解决常量时间分页问题。
|