<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-cn">
		<id>http://wiki.tomtalk.net/index.php?action=history&amp;feed=atom&amp;title=%E4%B8%80%E6%AC%A1php%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5</id>
		<title>一次php应用的优化实践 - 版本历史</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.tomtalk.net/index.php?action=history&amp;feed=atom&amp;title=%E4%B8%80%E6%AC%A1php%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5"/>
		<link rel="alternate" type="text/html" href="http://wiki.tomtalk.net/index.php?title=%E4%B8%80%E6%AC%A1php%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5&amp;action=history"/>
		<updated>2026-06-08T15:44:03Z</updated>
		<subtitle>本wiki的该页面的版本历史</subtitle>
		<generator>MediaWiki 1.24.2</generator>

	<entry>
		<id>http://wiki.tomtalk.net/index.php?title=%E4%B8%80%E6%AC%A1php%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5&amp;diff=4518&amp;oldid=prev</id>
		<title>2016年8月27日 (六) 02:39 Tom</title>
		<link rel="alternate" type="text/html" href="http://wiki.tomtalk.net/index.php?title=%E4%B8%80%E6%AC%A1php%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5&amp;diff=4518&amp;oldid=prev"/>
				<updated>2016-08-27T02:39:29Z</updated>
		
		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;之前做过的一次优化实践，最近翻出来看看，有些通用的优化手段还是可以复用的。系统跑得时间长了，总会出现这样那样的问题和瓶颈，有了问题不可怕，我们有“打虎”的家伙事儿－－无非就是&lt;br /&gt;
&lt;br /&gt;
# 定位问题&lt;br /&gt;
# 分析问题&lt;br /&gt;
# 提出解决方案&lt;br /&gt;
# 实践&lt;br /&gt;
# 结果反馈&lt;br /&gt;
# 总结再优化&lt;br /&gt;
&lt;br /&gt;
问题描述：系统采用 PHP5 + Zend framework 开发，在数据规模和访问量增加后（千万级），出现了后台apache服务器负载过高的现象，在访问高峰时段(比如每天下班到晚上10点这一段时间，特别是周五)，机器CPU负载会飙升到170多，CPU负载过高造成处理请求也相应的变慢，所以亟需解决这个问题。&lt;br /&gt;
&lt;br /&gt;
问题分析：通过连续几天的观察和分析，当CPU使用率达到100%时，其中系统CPU使用率占据了很大的比例，用户CPU使用率倒不是很高，另外前端haproxy和squid cache的cpu负载很低，memcached和squid的hit ratio一般都能达到60%左右。&lt;br /&gt;
&lt;br /&gt;
分析backend的access-log，发现相当大一部分请求的User-Agent是搜索爬虫；&lt;br /&gt;
&lt;br /&gt;
同时，在apache上配置了xdebug,在空闲时段对主要的页面的测量了一组性能数据，通过使用kcachegrind对测得的数据进行分析（如何配置xdebug，可以用soso搜一下），发现：&lt;br /&gt;
&lt;br /&gt;
* 性能数据不够稳定，同样的请求之间测试数据会相差比较大&lt;br /&gt;
* 慢的点比较分散&lt;br /&gt;
* memcached的访问大部分的情况都比较慢(100ms以上)&lt;br /&gt;
&lt;br /&gt;
解决方案通过上述初步的分析，对现有的程序逐步作了一系列调整。&lt;br /&gt;
&lt;br /&gt;
首先考虑到的是是否可以想办法增加前端squid cache的Hit ratio，从而减少穿透squid到达后端apache的请求数。&lt;br /&gt;
&lt;br /&gt;
考虑到相当一部分请求来源于Crawler，而之前squid cache只会对设置了language cookie的请求作cache，而来自Crawler的请求都没有cookie信息。于是想到把来自Crawler的请求都默认为language为zh_CN的，然后修改haproxy的配置，把User-Agent为常见的Crawler的请求都转交给squid cache.&lt;br /&gt;
&lt;br /&gt;
修改php代码，把一些页面的缓存时间设置得更长一些。&lt;br /&gt;
&lt;br /&gt;
经过如上两个步骤，到达apache的请求确实减少了一些，但是这个对cpu负载过高的问题帮助很少，于是另寻它法。&lt;br /&gt;
&lt;br /&gt;
其次，根据使用xdebug profiling的结果来看，和memcached的交互耗时比较长，于是想是否可以想办法让memcached能更快地响应请求，从而使得每一次请求能更快完成，从而使并发降低。&lt;br /&gt;
&lt;br /&gt;
通过代码分析，发现线上memcached使用的是poll()，而memcached的连接数在繁忙的时候保持在1000左右，memcached的CPU使用率在30%左右。很显然，poll()方式在处理如此多的并发连接时是很低效的。于是重新编译memcached，使其使用epoll()的方式来处理请求，替换为epoll之后，memcached的cpu usage从30%左右降低到3%左右，10倍之多！&lt;br /&gt;
&lt;br /&gt;
另外，memcached的hit ratio不是特别高，而且被换出的item数也比较高，于是想到对cache的内容作partition.原本打算做manually partition,后来发现php的最新的memcache扩展就能支持根据cache的key自动作partition,而且能在不修改程序代码(需要修改配置文件:-))的情况下增加新的memcached实例。于是升级每一个apache的php memcache扩展，然后再配置文件中增加了一台新的memcached。到此完成memcached的内容partition。修改之后的效果比较显著，页面的载入时间比修改前缩短了很多。&lt;br /&gt;
&lt;br /&gt;
经过这两步的调整，memcached的效率比以前高了，但是apache的负载仍然居高不下，没辙，再想其它办法!&lt;br /&gt;
&lt;br /&gt;
进一步深入分析前面说到主要系统CPU占用很高，要找原因只能深入内核了:) 从现在开始了我们的strace之旅。套用一句Nike的广告词：Just strace it!&lt;br /&gt;
&lt;br /&gt;
在高峰时段对 httpd 进程进行了strace，方法不外乎如下这些&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
strace -p PID -c 得出 summary&lt;br /&gt;
strace -p PID -o output.log 写入文件，慢慢研究&lt;br /&gt;
strace -p PID -e trace=file 只看 filesystem 操作相关的 syscalls&lt;br /&gt;
strace -p PID -elstat64,stat64,open,getcwd 只跟踪这些 syscalls&lt;br /&gt;
…&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
从上述strace分析得到如下结论：&lt;br /&gt;
&lt;br /&gt;
* lstat64,stat64,open等 syscalls实在是多啊&lt;br /&gt;
* 上述 syscalls 占用时间确实不少！60%以上的时间都被它们抢了, orz&lt;br /&gt;
* 绝大多数 syscall 是失败的，真是屡败屡战啊&lt;br /&gt;
&lt;br /&gt;
有了上述数据，我们就找到了问题的方向了，那就是找这些毫无意义的系统调用是怎么来的。&lt;br /&gt;
&lt;br /&gt;
经过分析，这些是php要加载某一个类时，会去 include_path 中定义的一系列目录中搜寻该类对应的文件，挨个目录这么试过去，直到找到为止。嗯，这种方式显然是比较低效的，有没有更好的方式来完成这个事情呢？答案是肯定的，有！而且还有不止一种方法！&lt;br /&gt;
&lt;br /&gt;
调用require_once()时，参数写绝对路径(开始Guys write Zend Framework就不懂这个道理;后来才有更新))&lt;br /&gt;
&lt;br /&gt;
使用__autoload()对class进行lazy loading，也就是说真正需要的时候才去加载，而不是不管三七二十一把可能用到的类文件都require_once了。&lt;br /&gt;
&lt;br /&gt;
问题是找到了，但是要解决这个问题还面临着另一个问题。开发中代码都注意用绝对路径了，唯一可以改进的地方是改为lazy loading，但是 Zend Framework中大量的require_once采用相对路径，这个就是导致问题——这里我说的问题是本文我们谈论的CPU负载过高的问题——的根本原因。&lt;br /&gt;
&lt;br /&gt;
OK，既然问题找到了，动手解决。写个脚本自动生成 Class -&amp;gt; File Path 对应关系，生成代码中所有类和Zend Framework中所有类的对应关系文件。把代码中和Zend Framework库中所有的require_once都注释掉。然后进行详细的测试，然后上线。结果令人吃惊，负载降到了3以内！！问题解决。&lt;br /&gt;
&lt;br /&gt;
总结：&lt;br /&gt;
&lt;br /&gt;
写代码的人都知道，可能出问题的地方总会出问题，任何问题都会有个原因（哪怕暂时没有找到），从根上解决才是王道，解决什么问题不重要，希望大家能学习这个解决的思路，善于利用工具。&lt;/div&gt;</summary>
		<author><name>Tom</name></author>	</entry>

	</feed>