9月 172010
 

开源的LAMP (linux/Apache/Mysql/PHP) 平台是流行的web application platform,不少网站,包括海归网都是建立在此平台上.

在帮助海归网正式转移到LAMP平台之前, 笔者虽然长期做网站设计和开发方面的工作,但主要是在J2EE 或者 ASP.NET /ASP方面的应用, 具体是在application 层面, 架构设计主要考虑功能/可扩充性和scalability, 而网站反应速度/性能方面一般不是问题–或者流量不大,或者硬件架构足够强(hardware load balancer, cluster, dedicated web/middle tier/DB tier/team等). 在海归网, 让我有机会在LAMP环境下接触和学习到许多以前做应用或自己玩操作系统时难以接触到的问题– 除了系统和网络管理外, 有许多是关于性能优化和scalability方面的.

在这篇里分享一点积攒的LAMP环境下PHP网站的性能优化经验. (谢谢老狼和海归网提供的给我一个发挥点业余爱好的机会); 以后有时间谈谈scalability/availability等.

有许多中小网站都和海归网一样只有一台服务器(海归网有一台dedictaed的dell, 更多更差的网站shared hosting的则是几十几百个网站公用一台server), 而这台服务器需要身兼web server, application server,database server, firewall等等所有一切功能…在网站建设的初期只能在有限的硬件条件下最大限度地进行优化充分利用所有的资源保证基本的功能/性能和稳定性.

1. 编译php/apache/mysql 时的优化选项

一般来说, 用不着自己编译这些东西,直接下载binary packages就可以了–无论是debian 的apt-get 还是redhat的rpm /yum. 但是这些现成的安装包编译时一般并未根据你的服务器硬件配置做优化, 而通过选择合适的C编译器 flags 和其他选项,自己编译往往能使总体性能提高几个到几十个百分点.

推荐在编译之前, 设置 CFLAGS 环境变量:

export CFLAGS=”-march=pentium4 -O2 -pipe -msse2 -mfpmath=sse,387 -mmmx -fomit-frame-pointer”

-march , -msse2, -mfpmath, -mmmx 等都是根据自己的服务器CPU类型做的设置; 海归网服务器是个xeon CPU所以用上述标记; 老一些的GCC 编译器还可以加上 -prefer-nopix

一般linux用户都知道, 下载源代码后标准的编译安装过程是:

./configure [options]
make
make install

在运行./configure 时注意尽量避免编译自己不需要的模块或功能以免编译出来的东西过于臃肿.

./configure 时最好不要enable pix, 而是用 –disable-pix选项;

2. 软件版本

linux 核心版本最好在2.6以上, 因为在虚存/线程管理等关键功能上比2.4有了很大的改善,对系统性能影响较大.

有些版本的php/apache/mysql有比较严重的bug,导致系统严重不稳定. 比如 mysql 5.1.11会导致数据库表/索引频繁崩溃;最新的php5.2.5和php5.2.6 development snapshot 似乎有memory leak, 导致系统内存很快被用光服务器奇慢无比甚至死机等等.We learned it the hard way Sad

3. 使用google的tcmalloc 内存分配函数代替libc里的标准malloc.

google的开源性能优化工具包 perftool 被证明对提高应用程序性能确有帮助. tcmalloc是其中一个, 与标准的malloc相比, 在内存的分配上效率很高; 编译mysql时建议使用tcmalloc库–
简单来说, 只要在./configure 后生成的Makefile里改一下, 在连接库的那行最后面加上 -ltcmalloc即可.

如果已经使用的是已编译的binary,或者只想简单测试一下tcmalloc的效果, 可以用LD_PRELOAD环境变量制定运行时使用tcmalloc库; 例如在mysqld_safe里加入

export LD_PRELOAD=/usr/local/lib/libtcmalloc.so

确定程序是否使用tcmalloc库可以用lsof命令:

lsof -n| grep tcmalloc

我的经验是使用tcmalloc后mysql的性能确实得到了提高,但在php和apache上没什么效果.

4. PHP config

一个是打开 zlib_compression开关,这样网页内容被传输到浏览器之前会先被压缩,从而减少网站流量并且加快page load速度.

当然也可以不在php里压缩.而是在apache里用mod_deflate.

5. Apache config

MPM: apache标准MPM是prefork, 起一大堆进程每个进程处理一个请求. 如果可行, 尽量使用apache worker MPM, 多线程模式可以减少apache内存使用提高性能. 最新的event mpm在worker mpm的基础上进一步提高了性能,但是目前(截至apache 2.2.Cool尚处于实验阶段不推荐在production使用.

使用worker mpm要求编译apache时使用worker mpm选项, 并且php需要编译成thread-safe的,链接到mysql的thread-safe client library.

httpd.conf里一些影响性能的设置(不一一解释了, apache在线文档里都有):
— mpm的设置
— keep alive: 大部分设成 on 比较好; 个别网站设成off更好;这个需要自己测试
— keep alive time out: 1-2 秒.
— maxkeepaliveRequest: 设成比较大的数目, 比如 1000或2000.
— extended server status: disable
— allow override: 设成不允许 (也就是说尽量避免每个目录下的.htaccess文件)
— HostnameLookups Off
–LogLevel: 在production box上设成 error或crit

尽量少用mod_rewrite

除了用apache worker MPM + mod_php, 另一种常见的apache配置是用apache+ mod_fastCGI + php-fcgi; 使用fast CGI 的apache进程可以重用,另外还有安全上的一些特点.

顺便提一句, 虽然apache在web server里最有名, 但性能方面并不怎么样, 相比一些其他open source产品来说体积庞大臃肿占内存速度又慢. lighttpd是一个日渐流行的http server, 速度较apache快效率更高. 许多其他的基于scripting language的web app 例如ruby/python/perl都以lighttpd+fast cgi的模式运行.

6. mysql tunning

–mysql storage engine的选择: 最流行的是MyISAM 和INNODB. 前者是mysql初期的主要engine, 至今仍大量使用,优点是查询速度很快,并且具有全文检索等别的engine没有的功能. 但是不支持事务处理(transaction), 写操作效率也不高. innodb支持transaction和其他一些现代关系数据库的功能. 这个完全根据你的网站应用的需要了…如果涉及到ecommerce之类最好使用innodb.

–mysql 的一些系统参数的优化:

query_cache_size: 这个很重要; 太小了每个sql都得到硬盘数据库里找一次, 太大了占用内存过多影响系统其他部分. 另外query cache里相关的记录若被修改则所有cache的查询结果都会被清除.

此外, myisam 的 key_buffer, table_open_cache, sort_buffer_size, thread_cache_size, thread_concurrency, tmp_table_size,max_heap_table_size,join_buffer_size, max_connections等都影响performance. 此外.对于IINNODB, 还有一个很重要的参数是innodb_buffer_pool_size.

有一个简单的perl script可以帮助你调节上述参数: ( http://rackerhacker.com/mysqltuner/ ) .

对许多web 应用来说, 数据库往往是性能的瓶颈; 而数据库性能最常见的问题是索引问题–sql 查询/join字段不通过索引导致full table scan…可以在mysql的启动参数上加上 log-slow-queries 选项记录所有执行速度太慢的sql 语句. 甚至打开log-queries-not-using-indexes 记录所有没有使用索引的sql…然后根据情况解决: 或者给相关字段加上索引, 或者优化/改写应用程序里的sql语句.

一时写不完, 等下半篇再说说reverse proxy/ opcode cache/ data caching 等.

7. reverse proxy/cache

reverse proxy又称http accelerator是在web server 前面加一道proxy, 所有request先经过proxy, 如果是在proxy缓存内的内容则不用经过web server直接就从缓存取出内容返回给用户的浏览器, 否则转给后面的web server.这样大大减轻了web server 的负担,从而使web server可以专注与处理动态页面,而可以缓存的静态页面则由更加快速轻便的proxy直接返回给用户, 从而提高了系统的总体处理能力和响应速度.

老牌open source proxy 软件Squid就可以用来做reverse proxy/http accelerator.

Apache 的mod_proxy 和mod_cache也可以结合起来达到相同功能.

squid 稳定版本现在是2.6和3.0. 2.6尚不支持http 1.1. 2.5问题较多,建议不要使用.

有一个比较新的开源软件 varnish是专门的reverse proxy软件, 从架构和原理来说性能很好并且非常灵活, 通过一种简单而功能强大的编程语言VCL定制你的policy;问题是对于新手掌握VCL并写出安全而又符合自己需要的control script不是意见容易的事. 我试用了varnish一阵,可能是自己的VCL写的有问题, 在performance上还不如squid, 而且一不小心还会出错…所以暂时放到一边,有时间再慢慢研究. 但是长远来说, varnish在这个领域非常有潜力.

除了caching, reverse proxy 还可以用于集成不同平台的后台web server, 甚至用于作load balancing.

8. PHP opcode cache/accelerator/opcode optimizer

所谓php opcode cache, 就是把编译后的php opcode 缓存起来, 从而不必每个请求都编译一边, 大大提高了php的执行效率.

open source中APC, Turke MMCache(eAccelerator的前身)/eAccelerator是老牌的; 其中eAccelerator在众多评测中声誉甚佳, 并且还有 opcode优化, cache压缩以及很好的管理界面等优点. 但在海归网的服务器上, 或许是设置或者海归网应用本身的原因, eAccelerator的表现实在是强差人意,甚至还不如不用.

对几种opcode cache反复试用的结果, xcache的效果最好, 切切实实地加快了PHP的执行速度. 在网上搜索一下, Xcache的用户满意度都很高。 xcache目前还没有opcode 优化功能, 期望在这方面能更进一步。

9. data cache

对于server side的缓存,一般来说可以有几级cache:

用户请求–>reverse cache, 若命中, 则直接返回
否则 –> web server ;
查询数据首先查 data cache; 若命中则不用查询数据库
否则查询数据库–>DBMS首先查自己的query_cache, 若命中则直接返回结果不用到磁盘上进行查询

这里说的是data caching.

由于php web applicaiton基本上来说是一种shared-nothing的架构, 每次请求是与其他请求基本是互不相干的, 不像J2EE或.NET一样数据可以缓存在内存里(比如jaba servlet或asp.net的session), 所以缺省是无法在内存里缓存数据的而是每次请求都得重新查询一遍数据库; 为了减少或简化数据库的访问(因这往往是瓶颈), 一般的数据缓存方法有:

–数据存储在文件里
— 数据存储在数据库表里:把耗费资源的复杂查询或计算的结果放到简单的数据库表中供以后重用,
— 数据存在 shared memory里, 从而多个php进程在多次请求间可以共享.
— 数据存在专门的caching服务中.

从性能来说,存贮在shared memory里是最快的.

如果只有一台服务器, 那么可以利用eaccelerator/xcache提供的功能–除了cache php opcode之外, 这些工具也都提供了直接cache应用程序数据到内存里的功能. 例如, 使用xcache的 API很简单:

if (xcache_isset(“my_key”)) {
//从cache取出数据
$data = xcache_get(“my_key);

} else {
….
//把数据存到cache里
xcache_set(“my_key”, $new_data, 3600); // save data for 1 hour
}

但是如果是多台web server, 很多时候就无法在本地机器的内存里cache数据了,因为会有数据一致性的问题。这种情况下一个很好的解决方法是使用memcached.

memcached 是开源的分布式caching 服务,支持几乎所有主要编程语言,客户端接口包括php/java/puthon/ruby/.net/perl/c 等。客户通过tcp/ip协议访问memcached缓存。最早是live journel网站为了解决多服务器数据缓存问题开发的,现在在许多著名的大流量网站上被使用,例如facebook, wikimedia, slashdot, sourceforge等等。

php的memcached 接口是一个zend extension需要下载编译; 当然还需要下载安装memcached 本身。

10. easy traffic limiting with iptables, 禁止 hotlinks etc

这个本来跟服务器性能无关,但是由于经常网站变得很慢是由于一些网站蜘蛛(有些是搜索引擎,有些是些恶意蜘蛛或者到处乱抓网站内容的)无休止频繁的扫网站,搞得服务器不堪重负。 一般简单的方法是在apache里禁止user agent 或者禁止ip, 但效果不好。用iptables的recents模块可以在ip层对client进行限速。

举个例子, 以下这两条iptables规则:

iptables -I INPUT -p tcp –dport 80 -i eth0 -m state –state NEW -m recent –set

iptables -I INPUT -p tcp –dport 80 -i eth0 -m state –state NEW -m recent –update –seconds 600 –hitcount 2 -j DROP

的意思是说, 任何用户不能在600秒内访问本机器的web server (tcp 80端口)平均超过2 次。若超过则请求将被丢掉(客户的表现就是服务器没响应)。

也就是说, 来自同一个ip的蜘蛛或恶意攻击者将无法在短时间内对服务器发起大量连接请求导致服务器瘫痪。

另外, 由于直接在别的网站链接本站的图片会导致服务器流量过大, 降低服务器性能, 有时被迫在apache里禁止无本地referer的访问图像请求。 不过现在海龟网的图像文件已经转移到了另一个server上也就没有这个限制了。

匆匆草就,先睡了。 以后有时间再瞎聊下scalability与availabilty。

作者:mmpower 在 海归茶馆 发贴, 来自【海归网】 http://www.haiguinet.com

 回复

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>