自从 GAE 毕业后,新的计费模型开始了实行。
新版的计费模型主要瓶颈在于数据库操作次数,特别是读操作(Datastore Read Operations)以及“密集”操作(Datastore Small Operations)。
配额
在官方文档 Limits 中对于数据操作的描述如下:
High-Level Operation | Low-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 而付费 ;-)