正在加载...

GAE程序优化最佳实践(一)-处理工作最小化

December 2nd, 2010

原文:http://code.google.com/appengine/articles/scaling/minimize.html

App Engine 允许您使用Google提供的高性能的Web服务运行环境. 虽然环境本身已经提供了良好的性能可扩展性,但我们仍然可以使用一些方法来优化我们的应用程序,以提供用户体验减少系统资源的消耗,当然也节约了你的钞票。

这篇文章提供了一些技巧,帮助你优化代码,提高性能。使用key, key name,或 ID来检索实体数据

App Engine的数据存储依赖于BigTable,BitTable是为分布式计算所设计的,所以它的一些实现与传统的面向对象的数据库系统有所不同。比如, App Engine数据库直接读取数据非常高效,  System Status page 显示直接读取数据要比使用查询平均快四到五倍. 这就要求我们认真的考虑如何根据这个情况来设计我们的应用程序,以获取更好的性能。

为了能够直接读取数据, 我们需要用到实体的keys. 当一个实体第一次创建的时候,您可以选择一个字符串作为实体的主键名(key name). 如果你知道这个实体的某个属性是唯一的(比如email地址),那么你就可以用这个属性值来作为主键的名字。如果你没有指定主键名.存储系统会为这个实体创建一个唯一的ID,并使用这个ID来关联实体的主键(Key).

不过如何指定主键,存储系统都允许你通过主键(key)简单并高效的来检索数据。

 

  • Python
# To retrieve an entity given its corresponding Key object, pass it into get
# directly:

e
= db.get(key)

# You can fetch an entity given its key name using the Model class'
# get_by_key_name class method:

key_name
= 'Alfred.Smith@example.com';
e
= Employee.get_by_key_name(key_name);

# To fetch an entity given its numeric ID, use the Model class'
# get_by_id class method:

id
= 52234;
e
= Employee.get_by_id(id);
     

您甚至可以使用一个列表来批量获取实体数据

# The return value is a corresponding list of model instances, with None values
# when no entity exists for a corresponding Key.

#...
entities
= db.get([key1, key2, key3]);
     

Note: 现在还无法使用 JDO and JPA接口来批量获取或设置实体, 您必须使用 low-level datastore API for Java.

不要使用offset进行分页

 

分页是将大量数据呈现在用户面前的一种常用的手段。通过分页我们把数据切分成多个子集,我们可以在单个页面展示有限的数据,从而减少页面加载的时间。但需要的时候,用户可以通过上一页或下一页来查看更多数据。

许多GAE的开发者喜欢使用offset来实现分页。但这种方式存在很多的问题。

  1. 不能检索超过1000个实体
  2. 性能不高,每次检索都必须搜索所有实体,比如说,pagesize=10,每页显示10条数据,使用offset来分页,将搜索所有数据,然后从中过滤出合适的数据进行显示。

所幸,这里有一些好的分页的方法,比如 "How-To Do Paging on App Engine". 这种方式在每个实体中存储一个字符串属性,然后使用这个属性来判断那些实体需要被提取。使用这种方式,你不再需要提取不需要显示的实体,而且可以显示所有数据(不再受1000个实体的限制)。

另外一个有效的方法是,当实体的显示顺序不是十分重要的时候,我们可以使用 __key__ 属性来进行分页,

这两种方法都比使用offset来的快,具体实现方法请参考文章中的相关链接。

Note: 从GAE SDK 1.3.6开始已经取消了1000个实体的限制

减少读/写频率

在前面的章节里面,我们介绍了一条原则:只提取必须的数据。 这只是我们减少读写的原则中的一条。我们需要考虑哪些数据是必须的,并优化我们的实体设计模型和业务逻辑从而获取更好的数据处理能力。

这是完全有可能做到的,下面以文件列表操作为例。

由于Google App Engine不支持写入文件,所以我们需要使用datastore来存储文件的内容。

一种可能的设计就是将文件的元数据(比如:文件名,大小,创建时间等)和内容存放在一个实体里.但是我们知道,其实通常情况下,我们并不需要获取文件的内容,比如说我们列出所有文件的时候。将文件云数据和内容存放在同一个实体中,但我们获取文件信息的时候,将会浪费很多不必要的CPU时间和内存。

解决方法是将文件的云数据和内容使用两个实体类保持,实体1保存元数据,另外一个实体2保存实际的文件内容。实体1里面保存实体2的reference信息,这样但我们列出文件信息的时候,就可以不用去提取文件内容了。

当然,当我们讨论减少读取频率的时候,我们不得不提到memcache. App Engine的Memcache服务为我们提供了非常高效的分布式缓存系统。您可以存储字符串或者其他实体在里面,并且快速的将其读取出来,从而避免直接从数据存储中重复读取数据。Memcache是一种非常好的数据库读写的替代品,他可以防止由于读取时间过长而导致的数据冲突(contention)。但Memcache并不能完全替代datastore,因为它保存的数据是不稳定,并且随时都有可能被丢弃。 但Memcache的使用,确实可以大大的减少数据库的读写操作,从而提高程序的性能。更多内容请阅读: Effective memcache .

代码和环境缓存(Python runtime)

如果你使用Python运行环境,我们强烈建议在你的代码里包含一个main函数。感觉Python 运行环境中关于 "App Caching"的说明, 添加一个 main 函数,将使系统能够缓存key items(比如python脚本,全局变量),这些数据可以在下一个Request中重用。如果没有main函数的情况下,这些脚本将在每个request中重新加载并执行,从而浪费大量的资源,导致响应变慢。

App Engine 在内存中自动缓存重要的模块,所以你不需要太担心你的脚本使用了太多的模块。但是,正因为这些缓存的存在,您可能会发现一些预期的Reload或Reevaluated可能不会发生,您必须提起注意。

 



google reader 抓虾
bloglines my yahoo
哪吒 鲜果
* 更多订阅本站方式请看 订阅帮助