php版本(5.3,5.5,7.0)及运行模式(fast-cgi/fpm,apache模块)之间性能对比测试

php 7.0发布在即,一直以来有传言说php7性能有飞跃,于是做了一个测试。

测试环境

硬件  虚拟机2G内存
OS  CentOS 6.7 (kernel 2.6.32-573.7.1.el6.x86_64)
Web  Apache/2.2.15 (centos 6自带)

php版本选择

5.3.3, CentOS 6自带的版本
5.3.29, 官方5.3分支的最后一个版本,用于跟apache模块做对比
5.6.15,
7.0.0beta3

除了第一个CentOS自带5.3.3是apache模块之外,全部跑在fast-cgi (php-fpm) 模式下,通过apache模块mod_proxy_fcgi整合在apache中(整合方式)。每个版本配置一个虚拟站点,域名分别为 a.dom, b.com... 。

php编译参数

三个自编译版本的编译参数如下(在 /usr/loca 目录下,分别安装到子目录里)

'./configure' '--enable-fpm' '--prefix=/usr/local/php53' '--with-config-file-path=/usr/local/php53/etc' '--with-config-file-scan-dir=/usr/local/php53/etc/php.d' '--enable-exif' '--with-gd' '--with-mysql' '--with-mysqli' '--with-pdo-mysql'

'./configure' '--enable-fpm' '--prefix=/usr/local/php56' '--with-config-file-path=/usr/local/php56/etc' '--with-config-file-scan-dir=/usr/local/php56/etc/php.d' '--enable-exif' '--with-gd' '--with-mysql' '--with-mysqli' '--with-pdo-mysql'

'./configure' '--enable-fpm' '--prefix=/usr/local/php7' '--with-config-file-path=/usr/local/php7/etc' '--with-config-file-scan-dir=/usr/local/php7/etc/php.d' '--enable-exif' '--with-gd' '--with-mysql' '--with-mysqli' '--with-pdo-mysql'

[注] centos自带5.3.3配置参数略,有点长,而且很多模块动态编译成动态加载模块,编译参数里是with-out,所以参数价值不大,故从略。
php7已经移除mysql模块,所以其配置参数里的 --with-mysql 事实上没用,在实际编译中被忽略掉的。

php-fpm配置

php-fpm全部配置成最大20进程,apache也配置成最大20个进程

测试说明

在本机上使用ab测试,减少网络传输的影响,500次连接,并发10,记录 Requests per second(req/s, 以下不再指明),示例

ab -c 10 -n 500 -H "Host: c.dom" http://127.0.0.1/phpinfo.php

[注] 因为使用虚假的域名,所以通过 -H参数指定主机名Host(改host文件也是一样的效果)

测试过程1:phpinfo页面

静态html基准测试,将phpinfo页面的输出保存成html文件,每秒稳定在3000次以上(300并发以下基本上能稳定在3000次,开ab的-k参数的情况下)

(phpinfo页面测试意义其实不大)

版本           次数1       次数2        次数3
---------     --------   --------    ----------
5.3模块:       810         837         774
5.6:          517         635         663
7.0b3:        675         700         638

这里看出php7的性能并不突出,反而apache模块运行效率更高

测试过程2:新安装wordpress文章页

新安装wordpress,其自带的一篇文章页http://127.0.0.1/wordpress/?p=1

版本          次数1        次数2        次数3       次数4
---------    --------    --------    ---------  --------
5.3模块:      7.00        7.06        6.84       6.91
5.6:         7.54        7.55        7.48       4.55
7.0b3:       10.12       10.38       10.14      10.47

[小结]:5.6 较5.3略有增强,但差别很小;但php7较都有显示提高。

测试过程3:wordpress导入一批文章后的文章页

导入一批文章后,该测试里增加php5.3.29的fast-cgi版本

ab -c 10 -n 500 -H "Host: c.dom" http://127.0.0.1/wordpress/?p=6459
版本         次数1      次数2       次数3      次数4      次数5    平均值
---------   ------    -------    --------   -------   -----   -----
5.3模块:     5.76      5.60       5.66       5.64      5.82    5.696
5.3 fpm:    5.86      5.97       5.91       6.11      5.97    5.964
5.6:        6.57      6.62       6.65       7.35      5.49    6.536
7.0b3:      8.73      8.33       9.02       9.00      8.67    8.750

[小结]:延续前面的结果,php7比5.x有30%-50%提升,效果明显。

另外5.3的fastCGI及模块差别可以忽略,似乎不像有人说的fastCGI效率有多高。

php7性能提升幅度,似乎也不像鸟哥Laruence所说的翻倍以上的提升(第45页片子)但30%+的提升,也足够让人欣喜了

测试结论

就前面做的测试来看,php7确实比5.x版本有明显提升,值得在生产环境中部署(暂不考虑兼容性)。然而说前面测试结果来看,

附记*php的后向兼容性

按官方文档所示,php7在语言核心方面,变化几乎忽略。主要是彻底放弃php5.4以来已经声明“过时”的特性。

已知可能有较大影响的是 mysql_* 函数被移除,这就意味着使用mysql_*的一些旧应用将无法在php7上跑!一个可选的解决方案是,使用fastCGI,多php版本共存,迁就这些旧应用。

apache下php版本共存,可以参考CentOS 6.x/apache 2.2下php多版本共存探索(模块及fastcgi)/mod_proxy_fcgi实现

Microsoft SQL server性能优化必备工具

这里说的工具并不一定是独立软件

1 查看sql语句执行花费的时间、IO开销

在mssql客户端执行sql语句时,先在当前会话里执行下面语句

SET STATISTICS IO ON
SET STATISTICS TIME ON
也可一次性执行
set statistics io,time on

然后再执行sql语句,运行完成的消息框里将给出如下统计信息

SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。

SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 4 毫秒。

(100 行受影响)
表 'your_table'。扫描计数 2,逻辑读取 165 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

http://msdn.microsoft.com/zh-cn/library/ms190356.aspx

mysql对大表执行update速度慢时,试试改用insert可能会有意想不到的发现

实例:需要根据用户日志的ip地址计算出其地理地址

表结构:

用户日志表(200万条记录),其中address是待填充的字段:

CREATE TABLE `tmp_open_ip` (
  `email` varchar(60) NOT NULL DEFAULT '',
  `address` varchar(50) NOT NULL DEFAULT '',
  `ip` int(10) unsigned NOT NULL DEFAULT '0',
  KEY `email` (`email`),
  KEY `ip` (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

另ip地址数据库表(44万条记录)

CREATE TABLE `ip` (
`s` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '开始ip',
`e` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '结束ip',
`a` varchar(50) NOT NULL DEFAULT '',
KEY `s` (`s`),
KEY `e` (`e`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

需要根据用户日志表 tmp_open_ip 里的 ip字段到ip地址数据库表里查询出对应的地理地址,将地址填充到address字段。

使用如下update语句执行:

UPDATE tmp_open_ip AS u 
INNER JOIN ip 
ON u.ip BETWEEN ip.s AND ip.e 
SET u.address = ip.a

在笔者的电脑上运行了速度非常之慢,执行了一个多小时(4500s)都没有完,也不知道还要多久。

实在看不过去,于是想到使用insert 是否会快一些,于是重新导一张表 tmp_open_log 与tmp_open_log完全一致。

创建一张表 tmp_open_address,是insert的目标表,为了速度更快,没建索引:

CREATE TABLE `tmp_open_address` (
`email` varchar(60) NOT NULL DEFAULT '',
`address` varchar(50) NOT NULL DEFAULT '',
`ip` int(10) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8

执行insert 语句

insert into tmp_open_address (email,address,ip)
select l.email,ip.a,l.ip
 from  tmp_open_log as l inner join ip on l.ip between ip.s and ip.e ;
/* Affected rows: 2,543,124  Found rows: 0  Warnings: 0  Duration for 3 queries: 16.922 sec. */

不到17s!本来还想去倒杯水、稍事休息一下,结果已经执行完毕。

到本文写完时,前面的update语句已经执行了5000s,结束仍是遥遥无期。

所以,对于大数据量执行update时,可以考虑改用insert 语句实现,可能麻烦一些,但高速带来的收益远大于麻烦!

后记:

直接杀死了update进程,去看看update执行了多少:运行

SELECT * FROM `tmp_open_ip` where address!=''

结果只有 11,373 ,照这个速度,要运行N天....

mysql join查询时,参与join的字段字符集编码不同,对性能影响是巨大的

本文一直以来都只是个标题,在三个月后的今天,熬夜补上内容。

直接上代码:

建utf-8编码的表 t1:

CREATE TABLE IF NOT EXISTS `t1` (
  `name` varchar(50) NOT NULL DEFAULT '',
  KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

随便插入些数据,数量大一点,后面实验结果更清晰,偷个懒,构造随机字符串插入语句

insert into t1(name) 
select concat(
char(round((rand())*25)+97),
char(round((rand())*25)+65),
char(round((rand())*25)+65),
char(round((rand())*25)+97),
char(round((rand())*25)+65),
char(round((rand())*25)+65),
char(round((rand())*25)+97),
char(round((rand())*25)+65)
)

每次执行插入一条记录,用你熟悉的脚本(python,php,shell等都行)写个循环,执行一万次以上。

将该表复制成一个新表t2,删除一部分数据,1000条左右即可。(推荐使用phpMyAdmin)

再将t2复制为t3,并将字段改为gb2312编码。

使用一个left join语句,写一个语句,查出t2/t3比t1少了哪些记录。

语句很简单,如下:

SELECT SQL_NO_CACHE t1.name, t2.name
FROM t1
LEFT JOIN t2 ON t1.name = t2.name
WHERE t2.name IS NULL 
LIMIT 0 , 30

注意加入 SQL_NO_CACHE ,禁用mysql缓存。

先看编码一致的t2表,phpMyAdmin里执行结果:

显示行 0 - 29 ( 1,129 总计, 查询花费 0.0010 秒)

平均耗时大概为0.0010秒

SELECT SQL_NO_CACHE t1.name, t3.name
FROM t1
LEFT JOIN t3 ON t1.name = t3.name
WHERE t2.name IS NULL 
LIMIT 0 , 30

phpMyAdmin执行结果:

显示行 0 - 29 ( 30 总计, 查询花费 0.1871 秒)

差两个数量级!

查询语句解释:

t2

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL name 152 NULL 11129 Using index
1 SIMPLE t2 ref name name 152 test.t1.name 1 Using where; Using index; Not exists

t3

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL name 152 NULL 11129 Using index
1 SIMPLE t3 index NULL name 102 NULL 10000 Using where; Using index; Not exists

附带上本次实验的三个表及数据
实验用表导出备份test.sql

 

wordpress文件附件转移到另外服务器上实现性能提升/web与文件数据分离

在大型web架构里,通常会把web与文件分离在不同服务器上,以提升web系统的高效运行。
而wordpress,本身好像没有这方面的考虑,所以,但可以朝这方面努力,应该可以通过插件等机制实现。
以下是笔者刚想到的一个实现方案。
由于wordpress本身,添加文章后,其中引用文件都是以绝对链接的形式写在html里的,这点可以通过插件的形式,把其url域名替换成另一个域名。
另一个域名下并没有这些相应的文件,可以通过url重写将对不存在文件的请求重写的一个动态处理程序(一般都是php,当然也可以使用其它,如jsp等)上,该处理程序通过远程http请求(或使用ftp也可以)把源站点下的相应文件抓取,并存在在对应的位置上,路径结构与源站点完全一致。这样,就实现了两个站点,即源站点及文件镜像站点的数据同步。第一次请求(即上述的抓取过程),请求后,再通过web程序把抓取到的文件读取并输出给客户端,即要模拟对静态web请求的返回数据,让请求浏览器以感觉不到这其实是动态请求的文件。
这只是一个技术方案,目前并没有实施,如有兴趣,可以自行实施,或者与笔者联系。笔者也可能在尝试实施这一方案的实施。

---
这里是一个方案,基本上就是上文中“插件”实现的功能 http://tsov.net/separation-of-the-blog-and-accessories/
当然,下面的远程自动抓取并没有提到,更没有实现的了

-------------- 2011-03-20 ------------------
周末,写了一个简单的代码,可以实现上述抓取功能,代码参看附件。其实这个“抓取”功能,应该通过apache的代理功能就可以实现,ngix等web服务器好像也有(个人对IIS还是有一定了解,没有这种功能~~xx的MS),但没有研究过。在学习php,所以使用php写一个相应功能的程序。对于使用虚拟主机的web站点,可以尝试一下。

讲一下可能参考的使用实例:使用比较小的虚拟主机、或者主机商对流量限制比较严酷,买了godaddy域名,就可获赠一个很大的虚拟主机,只是有广告。而广告只会附加到在html文档里,而对于图片、flash、音频视频是不可能带广告的,所以就可以把这些附件文件的“迁移”到godaddy虚拟主机上。使用这个方法,实际上只需要把文章内容里的文件地址修改到godaddy虚拟主机上,就可以了,不需要手工转移,自动化进行,省力。

代码并不完善,也没有使用说明,需要手工配置里面的代码。有兴趣的同学,可以自己看代码,当然前提是要有点php编程基础。这个代码将来可能会再做修改,请持续关注此文。http://www.path8.net/tn/archives/4217

源程序:点此下载源程序prx
2011-03-20