[转]小心,apc可能导致php-fpm罢工!

[原文链接]http://blogread.cn/it/article/7109?f=wb
最近线上产品出现了502错误。一般出现502错误,都是php-fpm 进程处理请求时出现异常导致的。

首先,查看了php-fpm 的进程数。发现php-fpm的进程数已经到达了php-fpm.conf中设置的最大值。最近流量没有特别的变动,这么多php-fpm进程,肯定有问题。

然后,用pstack命令查看这些php-fpm进程都在干啥。

[hailong.xhl@s010002.cm8 ~]$ sudo pstack 11740

Thread 3 (Thread 0x413bb940 (LWP 11741)):

#0 0x0000003cf92d4018 in epoll_wait () from /lib64/libc.so.6

#1 0x00007f4a0ffec116 in epoll_poll ()

#2 0x00007f4a0ffea81d in ez_run () from /home/admin/opt/easy/lib/libeasy.so.0

#3 0x00007f4a0ffe5b63 in easy_io_on_thread_start ()

#4 0x0000003cf9e064a7 in start_thread () from /lib64/libpthread.so.0

#5 0x0000003cf92d3c2d in clone () from /lib64/libc.so.6

Thread 2 (Thread 0x425a2940 (LWP 11742)):

#0 0x0000003cf92d4018 in epoll_wait () from /lib64/libc.so.6

#1 0x00007f4a0ffec116 in epoll_poll ()

#2 0x00007f4a0ffea81d in ez_run () from /home/admin/opt/easy/lib/libeasy.so.0

#3 0x00007f4a0fff1772 in easy_baseth_on_start ()

#4 0x0000003cf9e064a7 in start_thread () from /lib64/libpthread.so.0

#5 0x0000003cf92d3c2d in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7f4a17438710 (LWP 11740)):

#0 0x0000003cf9e0d174 in __lll_lock_wait () from /lib64/libpthread.so.0

#1 0x0000003cf9e08aca in _L_lock_1034 () from /lib64/libpthread.so.0

#2 0x0000003cf9e0898c in pthread_mutex_lock () from /lib64/libpthread.so.0

#3 0x00007f4a135eeb19 in apc_pthreadmutex_lock ()

#4 0x00007f4a135e7fb8 in apc_cache_find_slot ()

#5 0x00007f4a135e8323 in apc_cache_find ()

#6 0x00007f4a135edd27 in my_compile_file ()

#7 0x000000000050c651 in phar_compile_file ()

#8 0x00000000006185ec in zend_execute_scripts ()

#9 0x00000000005c778d in php_execute_script ()

#10 0x00000000006a592d in main ()

根据上面的堆栈信息,我们可以肯定php的apc模块申请互斥锁权限时,一直获取不到互斥锁权限,一直等待,导致卡死了。

接下来,我们查下,是什么导致apc模块一直拿不到互斥锁权限。我们使用gdb排查,最终查出互斥锁的权限被进程号11274的进程占用着。查杀步骤如下:

[hailong.xhl@s010002.cm8 ~]$ sudo gdb -p 26748

(gdb) thread 2

(gdb) bt

#0 0x0000003cf9e0d174 in __lll_lock_wait () from /lib64/libpthread.so.0

#1 0x0000003cf9e08aca in _L_lock_1034 () from /lib64/libpthread.so.0

#2 0x0000003cf9e0898c in pthread_mutex_lock () from /lib64/libpthread.so.0

#3 0x00007f4a135eeb19 in apc_pthreadmutex_lock (lock=0x7f4a0446e088) at /home/admin/APC-3.1.9/apc_pthreadmutex.c:72

#4 0x00007f4a135e7fb8 in apc_cache_find_slot (cache=0x27fc100, key=

{data = {file = {device = 43379392, inode = 48}, user = {identifier = 0x295eac0 “/home/admin/wanke/wanke_htdocs/openapi/index.php”, identifier_len = 48}, fpfile = {fullpath = 0x295eac0 “/home/admin/wanke/wanke_htdocs/openapi/index.php”, fullpath_len = 48}}, h = 5063011506047779259, mtime = 1392709731, type = 3 ’03′, md5 = “0200000000000032*’0000″}, t=1392709731) at /home/admin/APC-3.1.9/apc_cache.c:640

#5 0x00007f4a135e8323 in apc_cache_find (cache=0x7f4a0446e088, key=

{data = {file = {device = 43379392, inode = 48}, user = {identifier = 0x295eac0 “/home/admin/xxxx/index.php”, identifier_len = 48}, fpfile = {fullpath = 0x295eac0 “/home/adminxxxxindex.php”, fullpath_len = 48}}, h = 5063011506047779259, mtime = 1392709731, type = 3 ’03′, md5 = “0200000000000032*’0000″}, t=0) at /home/admin/APC-3.1.9/apc_cache.c:695

#6 0x00007f4a135edd27 in my_compile_file (h=0x7fffe1896080, type=8) at /home/admin/APC-3.1.9/apc_main.c:529

#7 0x000000000050c651 in phar_compile_file ()

#8 0x00000000006185ec in zend_execute_scripts ()

#9 0x00000000005c778d in php_execute_script ()

#10 0x00000000006a592d in main ()

(gdb) print (pthread_mutex_t *) 0x7f4a0446e088

$1 = (union {…} *) 0x7f4a0446e088

(gdb) print $1.__data.__owner

$11 = 11274

(gdb)

那么为啥11274进程一直占用互斥锁权限,不释放呢?最终发现这个进程已经退出了。11274的进程为啥会退出呢?在php-fpm 的日志中找到了答案。

[18-Feb-2014 15:48:45] NOTICE: [pool www] child 11274 started

[18-Feb-2014 15:48:45] WARNING: [pool www] child 11274 exited on signal 11 (SIGSEGV) after 0.089068 seconds from start

显然,11274进程运行过程中遇到了段错误,导致进程异常退出了。继续追查,发现是php的hsf扩展在启动初始化的时候遇到内存问题,导致段错误。

现在原因已经很明确了,是因为php扩展hsf启动的时候发生内存错误,导致php-fpm进程异常退出。退出的进程当时拥有apc的互斥锁。但是退出时,由于是异常退出,没能释放互斥锁。导致php-fpm其他进程无法获取apc的互斥锁。导致死锁。

那么如何解决呢?

1.与惠新宸(apc维护者之一)沟通,他建议不要再使用apc,建议换成opcache。因为apc已经基本不再维护。之前也做过opcache和apc的对比。可以看下这篇文章。

php的-zend-opcache-vs-apc-性能比较

2.hsf相关的问题,已经提交相关开发人员去完善。

3.如果你对apc情有独钟,不离不弃。那建议看看下面的文章,自己尝试修改下源码修复这个问题。

https://bugs.php.net/bug.php?id=46025

https://bugs.php.net/patch-display.php?bug_id=59281&patch=APC-3.1.9-bailout_deadlock.patch&revision=latest

[转]PHP7 快速编译安装

PHP7正式版发布啦, 之前没有安装过的,都来安装试一试 。 即将发布的ThinkPHP5 在PHP7环境下也完全兼容, 佩服鸟哥把兼容性做得这么好[good]
快速编译安装PHP7步骤:

第一步: 安装必要一些依赖
# yum install php-mcrypt libmcrypt libmcrypt-devel libxml2-devel openssl-devel libcurl-devel libjpeg.x86_64 libpng.x86_64 freetype.x86_64 libjpeg-devel.x86_64 libpng-devel.x86_64 freetype-devel.x86_64 libjpeg-turbo-devel libmcrypt-devel mysql-devel -y

第二步: 下载php7源码包
# wget http://59.108.200.35/files/1164000003730786/cn2.php.net/distributions/php-7.0.0.tar.gz
# tar -xzvf php-7.0.0.tar.gz
# cd php-7.0.0
第三步: 编译安装
# ‘./configure’ ‘–prefix=/usr/local/php7’ ‘–with-config-file-path=/usr/local/php7/etc’ ‘–with-mcrypt=/usr/include’ ‘–enable-mysqlnd’ ‘–with-gd’ ‘–with-iconv’ ‘–with-zlib’ ‘–enable-bcmath’ ‘–enable-shmop’ ‘–enable-sysvsem’ ‘–enable-inline-optimization’ ‘–enable-mbregex’ ‘–enable-fpm’ ‘–enable-mbstring’ ‘–enable-ftp’ ‘–enable-gd-native-ttf’ ‘–with-openssl’ ‘–enable-pcntl’ ‘–enable-sockets’ ‘–with-xmlrpc’ ‘–enable-zip’ ‘–enable-soap’ ‘–with-gettext’ ‘–with-curl’ ‘–with-jpeg-dir’ ‘–with-freetype-dir’ ‘–with-mysqli’ ‘–enable-embedded-mysqli’ ‘–with-pdo-mysql’

# make
# make install

第四步: 配置php

cp php.ini-production /usr/local/php7/etc/php.ini

cp sapi/fpm/init.d.php-fpm /etc/init.d/php7-fpm

chmod +x /etc/init.d/php7-fpm

cp /usr/local/php7/etc/php-fpm.conf.default /usr/local/php7/etc/php-fpm.conf

cp /usr/local/php7/etc/php-fpm.d/www.conf.default /usr/local/php7/etc/php-fpm.d/www.conf

vim /usr/local/php7/etc/php.ini

# 加入

zend_extension=/usr/local/php7/lib/php/extensions/no-debug-non-zts-20151012/opcache.so

# 启动

/etc/init.d/php7-fpm start

# 查看PHP版本

/usr/local/php7/bin/php -v

为了安全最好是去掉头信息 X-Powered-By: PHP/7.*.*

则修改 php.ini 文件 设置 expose_php = Off

vim /usr/local/php7/etc/php.ini

找到 expose_php = On

改为 expose_php = Off

PHP7: 新特性 – Manual

参照手册:http://php.net/manual/zh/migration70.new-features.php
标量类型声明

标量类型声明 有两种模式: 强制 (默认) 和 严格模式。 现在可以使用下列类型参数(无论用强制模式还是严格模式): 字符串(string), 整数 (int), 浮点数 (float), 以及布尔值 (bool)。它们扩充了PHP5中引入的其他类型:类名,接口,数组和 回调类型。

6
[1] => 15
[2] => 24
)
完整的标量类型声明文档和示例可参见 返回值类型声明.

null合并运算符

由于日常使用中存在大量同时使用三元表达式和 isset()的情况, 我们添加了null合并运算符 (??) 这个语法糖。如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数。


太空船操作符(组合比较符)

太空船操作符用于比较两个表达式。当$a大于、等于或小于$b时它分别返回-1、0或1。 比较的原则是沿用 PHP 的常规比较规则进行的。

1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo “a” <=> “a”; // 0
echo “a” <=> “b”; // -1
echo “b” <=> “a”; // 1
?>
通过 define() 定义常量数组

Array 类型的常量现在可以通过 definedefine() 来定义。在 PHP5.6 中仅能通过 const 定义。


匿名类

现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。

logger;
}

public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}

$app = new Application;
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});

var_dump($app->getLogger());
?>
以上例程会输出:

object(class@anonymous)#2 (0) {
}
详细文档可以参考 匿名类.

Unicode codepoint 转译语法

这接受一个以16进制形式的 Unicode codepoint,并打印出一个双引号或heredoc包围的 UTF-8 编码格式的字符串。 可以接受任何有效的 codepoint,并且开头的 0 是可以省略的。

echo “\u{aa}”;
echo “\u{0000aa}”;
echo “\u{9999}”;
以上例程会输出:

ª
ª (same as before but with optional leading 0’s)

Closure::call()

Closure::call() 现在有着更好的性能,简短干练的暂时绑定一个方法到对象上闭包并调用它。

x;};
$getX = $getXCB->bindTo(new A, ‘A’); // intermediate closure
echo $getX();

// PHP 7+ code
$getX = function() {return $this->x;};
echo $getX->call(new A);
以上例程会输出:

1
1
为unserialize()提供过滤

这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。

false]);

// converts all objects into __PHP_Incomplete_Class object except those of MyClass and MyClass2
$data = unserialize($foo, [“allowed_classes” => [“MyClass”, “MyClass2”]);

// default behaviour (same as omitting the second argument) that accepts all classes
$data = unserialize($foo, [“allowed_classes” => true]);
IntlChar

新增加的 IntlChar 类旨在暴露出更多的 ICU 功能。这个类自身定义了许多静态方法用于操作多字符集的 unicode 字符。


以上例程会输出:

Fatal error: Uncaught CustomError: Some error message
关于这个特性的完整说明,包括如何在开发和生产环境中配置它,可以在assert()的 expectations section章节找到。

Group use declarations

从同一 namespace 导入的类、函数和常量现在可以通过单个 use 语句 一次性导入了。


Generator Return Expressions

This feature builds upon the generator functionality introduced into PHP 5.5. It enables for a return statement to be used within a generator to enable for a final expression to be returned (return by reference is not allowed). This value can be fetched using the new Generator::getReturn() method, which may only be used once the generator has finishing yielding values.

getReturn(), PHP_EOL;
以上例程会输出:

1
2
3
Being able to explicitly return a final value from a generator is a handy ability to have. This is because it enables for a final value to be returned by a generator (from perhaps some form of coroutine computation) that can be specifically handled by the client code executing the generator. This is far simpler than forcing the client code to firstly check whether the final value has been yielded, and then if so, to handle that value specifically.

Generator delegation

Generators can now delegate to another generator, Traversable object or array automatically, without needing to write boilerplate in the outermost generator by using the yield from construct.


以上例程会输出:

1
2
3
4
Integer division with intdiv()

The new intdiv() function performs an integer division of its operands and returns it.


以上例程会输出:

int(3)
Session options

session_start() now accepts an array of options that override the session configuration directives normally set in php.ini.

These options have also been expanded to support session.lazy_write, which is on by default and causes PHP to only overwrite any session file if the session data has changed, and read_and_close, which is an option that can only be passed to session_start() to indicate that the session data should be read and then the session should immediately be closed unchanged.

For example, to set session.cache_limiter to private and immediately close the session after reading it:

‘private’,
‘read_and_close’ => true,
]);
?>
preg_replace_callback_array()

The new preg_replace_callback_array() function enables code to be written more cleanly when using the preg_replace_callback() function. Prior to PHP 7, callbacks that needed to be executed per regular expression required the callback function to be polluted with lots of branching.

Now, callbacks can be registered to each regular expression using an associative array, where the key is a regular expression and the value is a callback.

CSPRNG Functions

Two new functions have been added to generate cryptographically secure integers and strings in a cross platform way: random_bytes() and random_int().

list() can always unpack objects implementing ArrayAccess

Previously, list() was not guaranteed to operate correctly with objects implementing ArrayAccess. This has been fixed.

add a note add a note
User Contributed Notes 3 notes

up
down
65 donotreply at example dot com ¶28 days ago
the code in the ‘unserialize’ section is missing a closing right bracket

That is, the current:
$data = unserialize($foo, [“allowed_classes” => [“MyClass”, “MyClass2”]);
should be:
$data = unserialize($foo, [“allowed_classes” => [“MyClass”, “MyClass2”]]);

please fix the error and then delete this note once done. Thanks.
up
down
24 jay ¶1 day ago
Spaceship operator example is awesome, but should probably not be numbers that literally subtract to become the expected result, someone could misunderstand.
up
down
-111 109627685 at qq dot com ¶17 days ago
太空船操作符用于比较两个表达式。当$a大于、等于或小于$b时它分别返回-1、0或1。 比较的原则是沿用 PHP 的常规比较规则进行的。
和示例代码不符
是不是应该是
太空船操作符用于比较两个表达式。当$a大于、等于或小于$b时它分别返回1、0或-1。 比较的原则是沿用 PHP 的常规比较规则进行的。

【转】Nginx+PHP-FPM优化技巧总结

原文链接:http://blog.csdn.net/dc_726/article/details/12340349
php-fpm的安装很简单,参见PHP(PHP-FPM)手动编译安装。下面主要讨论下如何提高Nginx+Php-fpm的性能。

1.Unix域Socket通信

之前简单介绍过Unix Domain Socket这种通信方式,参见:Nginx+PHP-FPM的域Socket配置方法

Unix域Socket因为不走网络,的确可以提高Nginx和php-fpm通信的性能,但在高并发时会不稳定。
Nginx会频繁报错:
connect() to unix:/dev/shm/php-fcgi.sock failed (11: Resource temporarily unavailable) while connecting to upstream

可以通过下面两种方式提高稳定性:
1)调高nginx和php-fpm中的backlog
配置方法为:在nginx配置文件中这个域名的server下,在listen 80后面添加default backlog=1024。
同时配置php-fpm.conf中的listen.backlog为1024,默认为128。
2)增加sock文件和php-fpm实例数
再新建一个sock文件,在Nginx中通过upstream模块将请求负载均衡到两个sock文件背后的两套php-fpm实例上。

________________________________________

2.php-fpm参数调优

2.1进程数

php-fpm初始/空闲/最大worker进程数
pm.max_children = 300
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35

2.2最大处理请求数

最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn一个新的。
这个配置的主要目的是避免php解释器或程序引用的第三方库造成的内存泄露。
pm.max_requests = 10240

2.3最长执行时间

最大执行时间在php.ini和php-fpm.conf里都可以配置,配置项分别为max_execution_time和request_terminate_timeout。
其作用及其影响参见:Nginx中502和504错误详解

________________________________________

3.php-fpm的高CPU使用率排查方法

3.1CPU使用率监控方法

1)top命令
直接执行top命令后,输入1就可以看到各个核心的CPU使用率。而且通过top -d 0.1可以缩短采样时间。
下面的sar貌似最短只能是1秒。

2)sar命令
sar和iostat命令的安装:
sysstat.x86_64 : The sar and iostat system monitoring commands
yum install -y sysstat.x86_64

执行sar -P ALL 1 100。-P ALL表示监控所有核心,1表示每1秒采集,100表示采集100次。
输出结果如下:
CPU %user %nice %system %iowait %steal %idle
all 85.54 0.00 5.69 0.00 0.00 8.76
0 74.75 0.00 25.25 0.00 0.00 0.00
1 98.00 0.00 2.00 0.00 0.00 0.00
2 89.22 0.00 3.92 0.00 0.00 6.86
3 91.00 0.00 2.00 0.00 0.00 7.00
4 75.00 0.00 9.00 0.00 0.00 16.00
5 94.95 0.00 5.05 0.00 0.00 0.00
6 95.00 0.00 4.00 0.00 0.00 1.00
7 87.88 0.00 4.04 0.00 0.00 8.08
8 93.94 0.00 3.03 0.00 0.00 3.03
9 88.00 0.00 3.00 0.00 0.00 9.00
10 89.11 0.00 2.97 0.00 0.00 7.92
11 82.35 0.00 3.92 0.00 0.00 13.73
12 73.27 0.00 7.92 0.00 0.00 18.81
13 81.44 0.00 4.12 0.00 0.00 14.43
14 77.23 0.00 6.93 0.00 0.00 15.84
15 78.79 0.00 4.04 0.00 0.00 17.17

3.2开启慢日志

配置输出php-fpm慢日志,阀值为2秒:
request_slowlog_timeout = 2
slowlog = log/$pool.log.slow

利用sort/uniq命令分析汇总php-fpm慢日志:
[root@b28-12 log]# grep -v “^$” www.log.slow.tmp | cut -d ” ” -f 3,2 | sort | uniq -c | sort -k1,1nr | head -n 50
5181 run() /www/test.net/framework/web/filters/CFilter.php:41
5156 filter() /www/test.net/framework/web/filters/CFilterChain.php:131
2670 = /www/test.net/index.php
2636 run() /www/test.net/application/controllers/survey/index.php:665
2630 action() /www/test.net/application/controllers/survey/index.php:18
2625 run() /www/test.net/framework/web/actions/CAction.php:75
2605 runWithParams() /www/test.net/framework/web/CController.php:309
2604 runAction() /www/test.net/framework/web/filters/CFilterChain.php:134
2538 run() /www/test.net/framework/web/CController.php:292
2484 runActionWithFilters() /www/test.net/framework/web/CController.php:266
2251 run() /www/test.net/framework/web/CWebApplication.php:276
1799 translate() /www/test.net/application/libraries/Limesurvey_lang.php:118
1786 load_tables() /www/test.net/application/third_party/php-gettext/gettext.php:254
1447 runController() /www/test.net/framework/web/CWebApplication.php:135

参数解释:
sort: 对单词进行排序
uniq -c: 显示唯一的行,并在每行行首加上本行在文件中出现的次数
sort -k1,1nr: 按照第一个字段,数值排序,且为逆序
head -10: 取前10行数据

3.3用strace跟踪进程

1)利用nohup将strace转为后台执行,直到attach上的php-fpm进程死掉为止:
nohup strace -T -p 13167 > 13167-strace.log &

参数说明:
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-o filename,则所有进程的跟踪结果输出到相应的filename
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认为40.
-e execve 只记录 execve 这类系统调用
-p 主进程号

2)也可以用利用-c参数让strace帮助汇总,非常方便非常强大!
[root@b28-12 log]# strace -cp 9907
Process 9907 attached – interrupt to quit
Process 9907 detached
% time seconds usecs/call calls errors syscall
—— ———– ———– ——— ——— —————-
56.61 0.016612 5 3121 read
11.11 0.003259 1 2517 715 stat
8.04 0.002358 7 349 brk
6.02 0.001767 1 1315 poll
4.28 0.001255 6 228 recvfrom
2.71 0.000796 1 671 open
2.54 0.000745 0 2453 fcntl
2.37 0.000696 1 1141 write
1.69 0.000497 1 593 13 access
1.37 0.000403 0 1816 lseek
0.89 0.000262 1 451 22 sendto
0.56 0.000163 1 276 208 lstat
0.49 0.000145 0 384 getcwd
0.31 0.000090 0 1222 fstat
0.28 0.000082 0 173 munmap
0.26 0.000077 0 174 mmap
0.24 0.000069 2 41 socket
0.23 0.000068 0 725 close
0.00 0.000000 0 13 rt_sigaction
0.00 0.000000 0 13 rt_sigprocmask
0.00 0.000000 0 1 rt_sigreturn
0.00 0.000000 0 78 setitimer
0.00 0.000000 0 26 26 connect
0.00 0.000000 0 15 2 accept
0.00 0.000000 0 39 recvmsg
0.00 0.000000 0 26 shutdown
0.00 0.000000 0 13 bind
0.00 0.000000 0 13 getsockname
0.00 0.000000 0 65 setsockopt
0.00 0.000000 0 13 getsockopt
0.00 0.000000 0 8 getdents
0.00 0.000000 0 26 chdir
0.00 0.000000 0 1 futex
—— ———– ———– ——— ——— —————-
100.00 0.029344 18000 986 total

ps:可以使用strace学习php解释器的解释执行过程

3.4加速PHP解释执行

如果自己的程序的确没有问题,只是执行了太多操作,没法再做优化了。则考虑使用APC或xcache等PHP加速器来减少CPU解释php文件的耗时。
这些PHP加速器在php文件第一次解释时会生成中间代码opcode,所以之后的执行会快很多,并且减少了一些CPU的运算。下面以xcache为例,
看下如何安装和配置。

安装xcache命令如下,./configure的参数好多不知道是做什么用的,官网上也没说明,所以只开启–enable-xcache了:
tar zxvf xcache-3.0.3.tar.gz
/usr/local/php/bin/phpize
./configure –with-php-config=/usr/local/php/bin/php-config –enable-xcache
make
make install

php.ini中配置如下,最重要的是标红的两个参数,一般推荐xcache.size根据php文件多少来定,xcache.count与CPU核心数相同:
[xcache.admin]
xcache.admin.enable_auth = Off
xcache.admin.user = “xcache”
xcache.admin.pass = “”

[xcache]
xcache.shm_scheme =”mmap”
xcache.size=1024M
xcache.count =16
xcache.slots =8K
xcache.ttl=0
xcache.gc_interval =0
xcache.var_size=16M
xcache.var_count =1
xcache.var_slots =8K
xcache.var_ttl=0
xcache.var_maxttl=0
xcache.var_gc_interval =300
xcache.test =Off
xcache.readonly_protection = Off
;xcache.readonly_protection = On
xcache.mmap_path =”/dev/zero”
;xcache.mmap_path =”/tmp/xcache”
xcache.coredump_directory =””
xcache.cacher =On
xcache.stat=On
xcache.optimizer =Off

[xcache.coverager]
;;xcache.coverager =On
;;xcache.coveragedump_directory =””

常见问题是启动php-fpm时会报错:
Cannot open or create file set by xcache.mmap_path, check the path permission or check xcache.size/var_size against system limitation
这是因为/tmp/xcache是一个文件,而不能创建成目录。

重启php-fpm服务后,用top命令观察会发现每个worker进程的VIRT(包含了swap区)都是xcache.size大小,但REQ变得很小了。
使用上面的配置在使CPU使用率的峰值时间变短了,但峰值时还是所有核心都会达到90%以上,不知道是不是哪里没有配置对。
另外高并发时,/dev/zero这种配置方式经常会导致Nginx 502错误。/tmp/xcache和开启readonly_protection则很稳定。

________________________________________

4.php程序性能监控

常用的方法就是开启xdebug的性能监控功能,将xdebug输出结果通过WinCacheGrind软件分析。
xdebug的安装和配合IDE调试的方法参见:Vim+XDebug调试PHP

php.ini中配置的这几项是输出性能信息的:
xdebug.auto_trace = on
xdebug.auto_profile = on
xdebug.collect_params = on
xdebug.collect_return = on
xdebug.profiler_enable = on
xdebug.trace_output_dir = “/tmp”
xdebug.profiler_output_dir =”/tmp”

这样XDebug会输出所有执行php函数的性能数据,但产生的文件也会比较大。可以关闭一些选项如collect_params、collect_return,
来减少输出的数据量。或者关闭自动输出,通过在想要监控的函数首尾调用xdebug函数来监控指定的函数。

输出的文件名类似cachegrind.out.1277560600和trace.3495983249.txt,可以拿到Windows平台下用WinCacheGrind进行图形化分析。
WinCacheGrind使用方法网上有很多介绍,这里就不详细说明了。

________________________________________

结束语

以上都是近期做php程序优化工作总结出的一些优化方法,还请经验丰富的大牛们提出更好的建议,共同交流、进步~

[转]php-mysql异步查询

原文链接:http://www.walu.cc/php/async-mysql-query.md
php5.3之后,mysqlnd驱动为php提供了异步请求mysql服务器的功能(mysql的驱动必须是mysqlnd才行)。
下面列出我的demo,以及异步查询和平常查询的时间消耗。我这只能说明使用方法,数据有点简单,欢迎大家使用线上数据做更详细的测试。
这个异步查询接口无法复用同一个链接,对于每一个query,需要使用一个完全独立的连接,对服务器 来说,这是一个资源消耗。

&lt;?php
 
$host       = '127.0.0.1';
$user       = 'root';
$password   = '';
$database   = 'test';
 
/**
 * 期望得到额结果
 * array(
 *  1 =&gt; int,
 *  2 =&gt; int,
 *  3 =&gt; int
 * )
 */
$result = array(1=&gt;0, 2=&gt;0, 3=&gt;0);
 
//异步方式[并发请求]
$time_start = microtime(true);
$links = array();
 
foreach ($result as $key=&gt;$value) {
    $obj = new mysqli($host, $user, $password, $database);
    $links[spl_object_hash($obj)] = array('value'=&gt;$key, 'link'=&gt;$obj);
}
$done = 0;
$total = count($links);
 
foreach ($links as $value) {
    $value['link']-&gt;query(&quot;SELECT COUNT(*) AS `total` FROM `demo` WHERE `value`={$value['value']}&quot;, MYSQLI_ASYNC);
}
 
do {
 
    $tmp = array();
    foreach ($links as $value) {
        $tmp[] = $value['link'];
    }
 
    $read = $errors = $reject = $tmp;
    $re = mysqli_poll($read, $errors, $reject, 1);
    if (false === $re) {
        die('mysqli_poll failed');
    } elseif ($re &lt; 1) {
        continue;
    }
 
    foreach ($read as $link) {
        $sql_result = $link-&gt;reap_async_query();
        if (is_object($sql_result)) {
            $sql_result_array = $sql_result-&gt;fetch_array(MYSQLI_ASSOC);//只有一行
            $sql_result-&gt;free();
            $hash = spl_object_hash($link);
            $key_in_result = $links[$hash]['value'];
            $result[$key_in_result] = $sql_result_array['total'];
        } else {
            echo $link-&gt;error, &quot;\n&quot;;
        }
        $done++;
    }
 
    foreach ($errors as $link) {
        echo $link-&gt;error, &quot;1\n&quot;;
        $done++;
    }
 
    foreach ($reject as $link) {
        printf(&quot;server is busy, client was rejected.\n&quot;, $link-&gt;connect_error, $link-&gt;error);
        //这个地方别再$done++了。
    }
} while ($done&lt;$total);
var_dump($result);
echo &quot;ASYNC_QUERY_TIME:&quot;, microtime(true)-$time_start, &quot;\n&quot;;
 
$link = end($links);
$link = $link['link'];
echo &quot;\n&quot;;
 
//平常顺序执行的时间
$time_start = microtime(true);
$result = array(1=&gt;0, 2=&gt;0, 3=&gt;0);
foreach ($result as $key=&gt;$value) {
    $sql_result = $link-&gt;query(&quot;SELECT COUNT(*) AS `total` FROM `demo` WHERE `value`={$key}&quot;);
    if (is_object($sql_result)) {
        $sql_result_array = $sql_result-&gt;fetch_array(MYSQLI_ASSOC);
        $sql_result-&gt;free();
        $result[$key] = $sql_result_array['total'];
    } else {
        echo &quot;error.\n&quot;;
    }
} 
var_dump($result);
echo &quot;COMMON_QUERY_TIME:&quot;, microtime(true)-$time_start, &quot;\n&quot;;

结果数据
测试结果实在我的mac air上跑的,只能反应一个对比情况,并不代表线上服务器的实际性能。
数据量 异步查询 普通查询
82168条总数据 0.038721084594727 0.064269065856934
263865条总数据 0.11696815490723 0.19273400306702