django性能与优化

参考:http://blog.csdn.net/youmypig/article/details/8161809 html

Django 性能测试——一个现实世界的例子

2008年4月28日linux

大约一个星期前,安德鲁 和  启动 一个新的 Django 打造的网站,站名叫 Hey!Wall 。这是一个按照社交网络中的“墙”的概念创建的社交网站,它为各种朋友提供了一个留言及分享照片、视频和连接的空间。数据库

咱们想对其进行性能评估,并进行一些服务器配置和代码修改来决定采起何种步骤进行改进。咱们使用 httperf进行了测试,并经过优化将其性能提升了整整一倍。apache

服务器和客户端

服务器一是一台 Slicehost 提供的 Xen VPS ,配有 256MB 内存,运行的是 Debian Etch 系统。部署在美国中西部。django

为了测试,采用了一台位于英国的 Xtraordinary Hosting 提供的 Xen VPS,做为客户端。一般咱们使用的 ADSL 访问互联网络,但这让咱们很难向服务器发起足够多的访问请求。使用链接良好的 VPS 做为客户端使咱们能够真正地对服务器加以考验。后端

服务器规格说明

很难确切地描述服务器的规格。该 VPS 配有 256MB 内存,与数个相似的 VPS 同居一台主机(大概是一台 装有 16GB 内存的 Quad Core 服务器)之上。假定装满了 256MB 切片的话,物理服务器上最多装有 64 台 VPS 。若是四个处理器都是 2.4GHz,那么共 9.6 GHz ,除以 64 获得最少 150MHz 的 CPU 。缓存

 Xen VPS 上,无需竞争你就能够得到稳定的内存和 CPU 分配,但一般 主机上任何空闲的 CPU 都将获得使用。若是在同一机器上的其它 VPS 处于空闲状态,你的 VPS 将可以使用更多的 CPU 。这也许意味着在测试期间使用了更多的 CPU ,即某些测试可能比其它的使用了更多的 CPU 资源。服务器

使用 httperf 评估性能

现有各式各样的网络性能测试工具,主要包括 ab (来自 Apache)Flood 和 httperf。咱们使用 httperf 并无任何特别理由。cookie

httperf 命令看起来以下所示:网络

httperf --hog --server=example.com --uri=/ --timeout=10 --num-conns=200 --rate=5

在该例中,咱们向 http://example.com/ 发起了 200 次访问请求,每秒最多 5 次。

测试计划

某些工具支持进程,能够模仿用户对网站提交任务。咱们使用了一种简单的“暴力”测试来了解该站点每秒可以处理多少请求。

该基本方法是发起必定数量的请求,可以判断服务器如何反应:状态 200 为成功,状态 500 为失败。提升频率(每秒制造的请求数量)而后再试一遍。若是开始返回大量的  500 ,则已经达到极限。

监测服务器资源

另外一个方面是要掌握服务器在内存和 CPU 使用方面的状况。要跟踪这一状况,咱们运行 top 并将输出记录为日志文件以供稍后查阅。该 top 命令以下所示:

top -b -d 3 -U www-data > top.txt

在该例中,咱们以用户 www-data 每三秒记录一次进程的日志信息。若是你想更加明确的指定目标,可使用 -p 1, 2, 3 ,而不是 -U username ,其中 一、2 和 3 是 pid(即要观测进程的进程ID)。

网页服务器为配有以 FastCGI processes 方式运行的 Python 2.5 的 Lighttpd 。尽管数据库的日志也是颇有用的信息,但咱们没有记录该进程(PostgreSQL)的日志。

另外一个有用的工具是 vmstat,特别是 swap 列显示了有多少内存进行了交换。交换的意思是你没有足够的内存,它是一种性能杀手。要想反复运行 vmstat 的话,必须指定每次检查间隔的秒数。如:

vmstat 2

使用 httperf 进行已认证访问

httperf 只是简单地向某个 URL 发出简单的 GET 请求,并下载  html  文本(但不包括任何媒体文件)。对公共/匿名(public/anonymous)网页发起的访问请求是件轻松的事情,但若是要访问须要登陆的页面怎么办呢?

httperf 能够传递请求头部信息。Django 身份校验( authentication)(由 django.contrib.auth 提供)使用的进程依赖于在客户端 cookie 中所保存的进程  id 。而客户端在请求的头部信息中传递 cookie 。你能够看到这一切是如何进行的。

登陆站点,查看 cookies 信息。其中应该有个相似 sessionid=97d674a05b2614e98411553b28f909de 的数值。要经过 httperf 传递该 cookie,可使用 --add-header 参数选项。如:

httperf ... --add-header='Cookie: sessionid=97d674a05b2614e98411553b28f909den'

当心头部信息以后的 n 。若是少了该字符,你的每一个请求可能都会返回超时信息。

测试哪一个页面?

考虑到这一点,咱们测试了网站的两个网页:

  1. 主页: 对主页的匿名访问

  2. “墙”: 对某个“墙”已认证访问,该网页包括从数据库获取的内容

事实静态与高度动态

对于匿名用户来讲,主页基本上是静态的,它只是简单的渲染某个模板而无需数据库的任何数据。

“墙”页面则很是动态,包括了从数据库获取的主要数据。该模板在被渲染时,针对不一样时区用户的日期设置“删除”了指向某些物件的连接,等等。某些“墙”包含了大约50个物件,在被优化前,大约要发起 80 条数据库查询。

第一次测试时,咱们运行了两个能够从 Django 接受请求 FastCGI 后端。

Home: 175 req/s (即每秒请求数量)Wall: 8 req/s.

经压缩的内容

第一个配置优化是使用 GZipMiddleware 激活输出的 gzip  压缩。性能轻微提升,没有大的变化。但不管如何为了节约带宽,这么作仍是值得的。

Home: 200 req/s.
Wall: 8 req/s.

更多进程,更短的队列

而后,咱们将 FastCGI 后端的数量从 2 个提高到 5 个。这项改进减小了 500 响应的数量,由于更多的请求能够由额外的后端来处理。

Home: 200 req/s.
Wall: 11 req/s.

更多的进程,更多的问题

从 2 到 5 的改进很是不错,所以咱们决定将  FastCGI 后端数量提高到 10 。性能却显著地 降低 了。

经查看服务器上的 vmstat ,能够看到缘由是出现了内存交换。太多的进程,每一个都为 Python 使用了内存,致使 VPS 内存耗尽,从而不得不从硬盘往返交换内存。

Home: 150 req/s.
Wall: 7 req/s.

基于此,咱们将 FastCGI 后端数量降回 5 以进行更多测试。

分析——时间耗到哪里去了

“墙”页面的性能使人失望,所以咱们开始进行优化。咱们所作第一件事情是分析代码以肯定时间都被花费在何处。

使用一些简单的 分析中间件 以后,很清楚地发现时间被消耗在数据库查询之上。“墙”页面包括许多查询,且数量与其所包含的物件数量呈正比。测试墙页面上引起了大约 80 个查询。毫无疑问其性能是糟糕的。

进行优化

经过优化物件附加媒体的处理方式,咱们直接给每一个物件剔除了一次查询。该措施稍微地减小了请求所需时间,所以也提升了每秒可处理的查询数量。

Wall: 12 req/s.

致使低效的另外一个缘由是不管页面是否被请求,对每一个物件的内容都应用了多个过滤器(Filter)。经咱们修改,被过滤内容的 HTML 输出都被存储在物件当中,节约了页面被查阅时的须要进程。这又带来一点小小改进。

Wall: 13 req/s.

经过减小数据库查询,咱们以修改用户配置文件(用于显示是谁将该物件粘贴到墙上)的获取方式为每一个物件剔除了一次查询。此次修改又提升了很多。

Wall: 15 req/s.

这场测试的最后一次优化目标是减小获取物件所附加媒体的查询数量。咱们再一次削减了一些查询,稍微地提升了性能。

Wall: 17 req/s.

下一步:缓存

在尽量地减小查询以后,接下来要进行一些缓冲工做。获取缓存数据一般比查询数据库更加快捷,所以咱们期待性能有一个显著提高。

对整个页面的输出进行缓存是没有意义的,由于每一个页面根据发出请求的用户不一样而大相径庭。只有当用户对同一页面的两次请求之间,状况没有发生任何变化,才可能出现缓存命中。

对墙、物件及用户的列表进行缓存的做用更大。被缓存的数据将被用于从同一用户发出的多个请求,及在对于墙壁的不一样程度和不一样用户访问之间共享。这未必是巨大的胜利,由于每一个“墙”极可能只有极少数的用户,而数据必须在高速缓存中停留足够长的时间以被别人获取。

在这种状况下,咱们简化的 httperf 测试将会被极大地误导。每一个请求都由同一用户发出,所以缓存命中几乎是100%,而性能将所以极高!这反映不出真实世界的站点使用状况,所以咱们最好进行一些更好的测试。

目前咱们尚未使用缓存,由于站点能够轻松地应对的当前活动水平,但一旦 Hey! Wall 流行起来,这将是咱们的下一个步骤。

多少用户可以致使每秒17次请求?

提供每秒 17 次请求相应看起来仍然很是少,但将该数据翻译成站点的实际用户量是很是有趣的事情。显然,这数据不包括提供像图片、CSS 和 JavaScript 文件之类的媒体文件服务。相对来讲,媒体文件个头要大一些,但它们直接由 Lighttpd (而不是 Django)处理,并提供了 Expires 头部信息来容许客户端对它们进行缓存。不过,为了在测试中更好地进行评估,咱们仍是须要对服务器进行一些处理。

如今说采用何种通用模式还为时过早,所以我说的只能是猜想。请容许我这么说!

我将假定每一个用户平均访问三个“墙”,并按顺序查看它们的内容,暂停10至20秒时间以阅读新的评论,或查看一些照片和打开一些连接。该用户天天进行三次这种操做。

只看墙页面,不看媒体的话,用户将天天对墙页面发起 9 次访问请求。每一个用户一次只能发起一次访问请求,所以在时间上,任何一秒内 17 个用户能够同时进行该操做。一分钟内,用户只发出3次访问请求,所以17个并发用户只用去了60 秒中的 3 秒(或20秒中的1秒)。

若是一段时间内用户的请求分布是彻底平衡的(提示:不可能的!),那也就意味着每分钟能够有 340 用户(17 * 20)访问该网站。延续这个不真实的例子,咱们能够说天天有 1440 分钟,而每一个用户天天访问网站 3 分钟,所以该网站能够应对大约 163,000 个用户。这对于一个每个月 20 美圆的 VPS 来讲已经很是棒了!

为了更多统计一下这些数字,让咱们假定天天6小时内,咱们每分钟应对 200 个并发用户,另 6 个小时内(每分钟)应对 100 个并发用户,剩下的 12 小时内(每分钟)应对 10 个并发用户。基于每秒 17 次请求的最大负荷,网站天天仍然能够应对的大约 115,000 个用户。

我确信这些数字并不虚假和荒谬。若是有人在评论中提出更好的评估方案或者真实世界的数据,我将很是感兴趣。

相关文章
相关标签/搜索