GAE 配额优化

  |   24 评论   |   32,954 浏览

自从 GAE 毕业后,新的计费模型开始了实行。

新版的计费模型主要瓶颈在于数据库操作次数,特别是读操作(Datastore Read Operations)以及“密集”操作(Datastore Small Operations)。

配额

在官方文档 Limits 中对于数据操作的描述如下:

High-Level OperationLow-Level Operations Required
Entity Get (per entity) 1 Read
New Entity Put (per entity, regardless of entity size) 2 Writes + 2 Writes per indexed property value + 1 Write per composite index value
Existing Entity Put (per entity) 1 Write + 4 Writes per modified indexed property value + 2 Writes per modified composite index value
Entity Delete (per entity) 2 Writes + 2 Writes per indexed property value + 1 Write per composite index value
Query 1 Read + 1 Read per entity returned
Query (keys only) 1 Read + 1 Small per entity returned
Key allocation (per key) 1 Small

* Hight-Level 对应的其实是 Low-level APIs 调用

目前的免费配额情况:

Datastore Write Operations 0.05 Million Ops
Datastore Read Operations 0.05 Million Ops
Datastore Small Operations 0.05 Million Ops

从中我们可以得知,正真的瓶颈是 Read 操作,因为一天只能有 5 万次调用。

特别是条件查询,假设返回结果集大小为 N,则实际是进行了 1 Read + N 次 Read 操作(1 Read + N per entity returned)。

读操作

例如 B3log Solo 首页渲染,文章列表部分用的是一个条件查询:

final Query query = new Query().setCurrentPageNum(currentPageNum).
                    setPageSize(pageSize).
                    addFilter(Article.ARTICLE_IS_PUBLISHED,
                              FilterOperator.EQUAL, PUBLISHED).
                    addSort(Article.ARTICLE_PUT_TOP, SortDirection.DESCENDING);

查询页号为 currentPageNum 的、页最多显示 pageSize 条记录、文章必须是已发布的、按置顶排序的文章列表。

假设满足条件的文章为 15 篇,则这次查询一共调用 16 次 Read 操作。

分页

分页需求是大多数应用的基础需求之一,分页所必须的参数是:

  • 页大小
  • 总记录数

然后我们可以计算出这两个参数计算出总页数(pageCount = ceil(sum/pageSize)),以便进行分页控制。

其中总记录数就是符合查询条件的结果集大小。GAE 上我们可以使用 count API

但是,count API 有个严重损耗配额的问题,count 执行相当于使用 keys 进行查询遍历。

假设满足查询条件的总记录数为 N,那么一次 count 调用相当于执行了 1 Read + N Small 次操作。

而通常情况下,N 会比较大。比如前面文章列表查询的例子中,如果我有 1,000 篇发布了的文章,那么一次 count 调用相当于使用了 1 Read + 1000 Small 操作配额。

也就是说,在不考虑缓存的情况下,访问有 1,000 篇文章博客的首页至少需要花费 2% 的 Small 配额,50 次请求今天的免费配额就玩完了。

优化策略

《GAE Java 应用性能优化》 一文中提到了缓存 HTML 页面以及缓存数据查询结果,这是比较通用的做法。而应用本身也需要考虑优化设计:

维护条件查询的总记录计数

还是以前面文章列表查询的例子来看,如果应用维护了已发布文章的总数,那也就不必调用 count 接口了,而只需要查询这个总数(1 Read)。

隐式分页

也可以换个思路,对于查询量大的地方使用隐式分页,也就是不计算完整的分页结果,而只是提供“下一页”/“上一页”的请求入口。

结论

新的计费模型让一些没有足够优化的 GAE 应用瞬间倒下,即使是在实现上进行了足够优化的应用,也必须在功能上进行一定的精简,否则要想完全使用免费配额也是不可能的。

在应用设计之初就应该考虑尽量少的依赖数据 API(例如 count),能在应用中维护的数据就应该维护住,虽然这可能比较繁琐,但这也为将来的优化提供了数据支撑。

另外,我觉得我们也应该为使用 GAE 而付费 ;-)

---- EOF ----
点击加入开源技术 Q 群 242561391,让学习和分享成为一种习惯!

评论

  • ANGO 回复»

    不懂代码,不懂GAE,指是简单的写写博客怎么这么难呢?

  • 乱世浮生 @88250 回复»

    不好意思,不知道怎么样是缓存失效,可否指教?

  • 88250 @乱世浮生 回复»

    打下日志看看,有可能是缓存失效。

  • 乱世浮生 @88250 回复»

    加了memcache,但测试看起来像是没生效,本地是生效了的,不知道为什么。

  • 乱世浮生 @88250 回复»

    看来得先用memcache优化下了,不知道效果如何。

  • 88250 @乱世浮生 回复»

    如果应用上的逻辑设计已经不可避免读多次了,就只能靠缓存来优化了。

  • 乱世浮生 回复»

    我的都没人访问,但读的配额已经花了32%,开始担心了……


    关键是我还不知道怎么去优化,代码写得烂,估计是评论嵌套惹的祸。


    想请教下,想这种一次查询都是1+n次读,是不是没办法优化了?

  • Ansen 回复»

    写得很不错呀,


    原来做优化这么辛苦,


    D辛苦了

  • 88250 @就是我 回复»

    用事务不会有问题的,不然 GAE 上面完全没有办法保证一致性了。

  • 就是我 @88250 回复»

    什么意思,


    我的BLOG采用的是统计表形式,不过在多实例,多线程的情况下,有时会有统计错误,这个统计错误是由于并发产生的,我用synchronized关键字解决多线程下的同步,用事物来控制多实例下的同步,但还是会遇到。

  • 88250 @就是我 回复»

    嗯,应用自己维护计数信息是正确的 ;-)。

  • 就是我 回复»

    GAE不是推荐用一个统计表吗?

  • zcq100 回复»

    不能通过include预加载来解决N+1的问题嘛?查询还可以再优化下呢?[em11]

  • 何必呢 @88250 回复»

    OK。3KS,优化做的真好。

  • 88250 @何必呢 回复»

    按照目前的配额看,5K PV 是完全没有问题的。这周末做个统计功能仔细分析一下 ;-)

  • 何必呢 @88250 回复»

    [em00] 每天大概有多少浏览啊。没别的意思,大致上想知道多大的博客放在GAE上不会超标。谢谢

  • 88250 @何必呢 回复»

    嗯,够用 ;-)

  • 何必呢 回复»

    你的这个Blog配额够用吗?

  • romotc @88250 回复»

    gae做blog还是靠谱的,加油[em00]

  • 88250 @romotc 回复»

    040 完全可以胜任目前的免费配额 ;-)

  • romotc 回复»

    木信心控制在免费配额了?[em13]

  • 1321252980414 回复»

    迟早要收费的,一个阴谋而已




    该评论来自
    B3log 社区
  • 88250 @yoyo 回复»

    看代码吧,开源的。

  • yoyo 回复»

    好文章!关于count我是用缓存来维护的,发表文章的时候清空这个缓存项。D是用什么维护的啊???

发表评论

validate