这几天看源码弄清了一件事:WEB服务器接收浏览器请求、将请求传给PHP/Python进程(FCGI等)、与数据库进行交互都是用socket(套接字)。
也就是说,这些行为都是进程间通信。一台WEB服务器在硬件、操作系统不变的情况下,它的性能主要取决于socket通信的速度。如果所有进程都在一台服务器上的话,这个速度就取决于通信的效率了。
例如与MySQL数据库交互时,程序会调用驱动程序来访问数据库,这个驱动程序主要做这几件事:
1.创建socket,连接到MySQL。
2.将程序调用的API翻译成SQL语句,通过socket发送给MySQL;MySQL执行后,将结果发送回来;驱动程序将结果(字符串)传给程序,如有需要,还可以自动翻译成程序能识别的变量类型(如整型)。
3.断开连接。
可见连接的速度、翻译的速度和接受响应的及时性是最主要的3个方面。
弄明白这点后就不难发现,与数据库的执行时间相比,这些只是很少的一部分;而且PHP和Python使用的都是C实现,而JDBC是用Java实现,所以根本不必担心PHP和Python的性能。不过在翻译方面还存在算法和实现上的差异,客户端还可以缓存一些语句,所以仍然会出现一些性能上的差异。为证明我的想法,我特意去测试了一番。
首先列出测试平台:
- CPU:Intel Core2 Duo T9400 @ 2.53GHz
- 内存:3GB
- 操作系统:Windows XP Pro SP2
- MySQL:5.1.36
- Java:1.6.0_17-b04
- JDBC:MySQL Connector/J 5.1.10
- PHP:5.2.11 (cli)
- MySQLi:5.2.11.11
- Python:2.6.4
- MySQL-Python:1.2.3c1
所用的库都是最新版的,也都采用了最为推荐的库。
但数据库并没有使用最新的稳定版,因为我懒得重下了。5.4.3-beta测试版也试过,在连续插入时,性能比5.1快1~2个数量级,估计是服务器端缓存和设置的原因。
测试项目:
- 创建100万个随机数,并生成插入这些随机数的SQL语句。
- 连接本地数据库,如不成功,尝试创建数据库。
- 删除并创建数据库表,引擎类型为InnoDB,主键为自动递增的整数,此外有个浮点型的字段(无索引)。
- 分成100组,每次插入1万个随机数。(因为每组的执行量都很大,因此启用自动提交事务。)
- 用SELECT COUNT(*)统计小于0.1的随机数个数。(约10万个)
- 用SELECT *取出再统计大于0.9的随机数个数。(约10万个)
- 将所有0.4~0.5之间的随机数加1。(约10万个)
- 将所有0.5~0.6之间的行删除。(约20万个)
- 断开数据库连接。
- 再次连接数据库。
每种测试3次(等硬盘灯无闪烁后才进行下一次测试),取最好的一次作为测试结果:
java:
- 测试数据量:1000000
- 测试引擎:InnoDB
- 共有99465个小于0.1的随机数
- 共有99859个大于0.9的随机数
- 创建随机数:2.367840
- 初次连接数据库:0.220420
- 再次连接数据库:0.013174
- 初始化数据库和表:0.075140
- 插入:12.139346
- 选择(COUNT):1.130345
- 选择:1.017769
- 更新:6.173245
- 删除:9.380070
- 关闭连接:0.002131
- 总时间:32.506307
php:
- 测试数据量:1000000
- 测试引擎:InnoDB
- 共有99898个小于0.1的随机数
- 共有100152个大于0.9的随机数
- 创建随机数:1.506294
- 初次连接数据库:0.003146
- 再次连接数据库:0.001808
- 初始化数据库和表:0.131754
- 插入:12.046944
- 选择(COUNT):1.236742
- 选择:1.238153
- 更新:6.115232
- 删除:8.145547
- 关闭连接:0.000125
- 总时间:30.423937
Python(MySQLdb):
- 测试数据量: 1000000
- 测试引擎: InnoDB
- 共有100040个小于0.1的随机数
- 共有100351个大于0.9的随机数
- 创建随机数: 1.6822107279
- 初次连接数据库: 0.0332120423126
- 再次连接数据库: 0.00221704155137
- 初始化数据库: 0.131054924578
- 插入: 11.7999030603
- 选择(COUNT) 1.27067266929
- 选择: 1.16714526567
- 更新: 6.29200638629
- 删除: 8.13660563005
- 关闭连接: 0.000131022238861
- 总时间: 30.5129417286
Python(_mysql):
- 测试数据量: 1000000
- 测试引擎: InnoDB
- 共有99745个小于0.1的随机数
- 共有99869个大于0.9的随机数
- 创建随机数: 1.68099074044
- 初次连接数据库: 0.0112056141213
- 再次连接数据库: 0.00159293988482
- 初始化数据库: 0.130169616529
- 插入: 12.1364623157
- 选择(COUNT) 1.125517908
- 选择: 0.968366649951
- 更新: 6.8042843434
- 删除: 8.9760508668
- 关闭连接: 9.61015995031e-05
- 总时间: 31.8331441566
可以看到,在大批量数据测试中,Java是最慢的,而PHP是最快的。
不考虑IO性能的波动的话,Java主要慢在连接和关闭数据库。JDBC 4.0在第一次连接数据库时会动态加载驱动,非常耗时,因此使用Java要记住使用数据库连接池,避免连接浪费大量时间。当然,这也造成了数据库的负担,势必影响内存占用。而创建随机数的算法实现各不相同,所以不具备可比性;令我惊讶的是SELECT的翻译速度,将字符串转换成浮点数居然慢于Python,要知道后者的浮点数可是对象。
PHP连接数据库非常快,所以完全无需使用连接池,因为维护连接池会增加复杂性。
Python的表现和PHP差不多,但是第一次连接数据库比较慢(仍比Java快1个数量级)。如果不用连接池,则使用FCGI等方式来运行比较合适。_mysql模块的通信很快,但更新和删除操作却不太理想,也许是IO性能波动的原因。此外,我在连接数据库时使用了转换参数,实际上我用的语句都不需要翻译,不使用的话会更快一点。
接着试试小数据,改成最常用的MyISAM引擎,插入100条(1组)。一般的应用不可能一次插入那么多,所以足够满足平时的应用了;而且由于数据量很小,也基本不受IO影响。
测试结果:
java:
- 测试数据量:100
- 测试引擎:MyISAM
- 共有9个小于0.1的随机数
- 共有10个大于0.9的随机数
- 创建随机数:0.001596
- 初次连接数据库:0.224135
- 再次连接数据库:0.018656
- 初始化数据库和表:0.055601
- 插入:0.001476
- 选择(COUNT):0.000529
- 选择:0.000433
- 更新:0.000304
- 删除:0.000313
- 关闭连接:0.000927
- 总时间:0.285314
PHP:
- 测试数据量:测试数据量:100
- 测试引擎:MyISAM
- 共有12个小于0.1的随机数
- 共有9个大于0.9的随机数
- 创建随机数:0.000649
- 初次连接数据库:0.008077
- 再次连接数据库:0.001609
- 初始化数据库和表:0.060421
- 插入:0.001860
- 选择(COUNT):0.000580
- 选择:0.000465
- 更新:0.000326
- 删除:0.000373
- 关闭连接:0.000127
- 总时间:0.072878
Python(MySQLdb):
- 测试数据量: 100
- 测试引擎: MyISAM
- 共有14个小于0.1的随机数
- 共有9个大于0.9的随机数
- 创建随机数: 0.000198907961766
- 初次连接数据库: 0.0334640296462
- 再次连接数据库: 0.00150577796899
- 初始化数据库: 0.0123194428342
- 插入: 0.00125211444471
- 选择(COUNT) 0.000581079438867
- 选择: 0.000484139744018
- 更新: 0.000250311142897
- 删除: 0.000262323842835
- 关闭连接: 7.98984228442e-05
- 总时间: 0.0488922474784
Python(_mysql):
- 测试数据量: 100
- 测试引擎: MyISAM
- 共有12个小于0.1的随机数
- 共有10个大于0.9的随机数
- 创建随机数: 0.000214273043082
- 初次连接数据库: 0.0118774872225
- 再次连接数据库: 0.00123702872851
- 初始化数据库: 0.0315031659052
- 插入: 0.00120322554962
- 选择(COUNT) 0.000596165155069
- 选择: 0.000507327048549
- 更新: 0.0002447238406
- 删除: 0.00026148574749
- 关闭连接: 5.78285787719e-05
- 总时间: 0.0464656820909
从结果可以看出,虽然差距都很小,但Python仍然稍微占优。不过Java的SELECT操作稍微胜出,而这也是实际应用中最常使用的操作。
再从语言方面来看,Python无疑是写得最欢畅的,生成随机数只用了1行代码;PHP的变量要写个$让我老是出错,不过数据库操作不需要处理异常,这点节省了很多代码;Java的代码量很大,而且不得不使用很多try...catch,我甚至懒得以安全的方式将close()放在finally块里面,因为它也会抛出我根本懒得去管的异常,且会提示我计时变量可能没有初始化。
总体上来看,Google放弃Python,只采用C++和Java是有点不明智。因为页面响应时间主要在于数据库通信和磁盘文件IO上,语言的影响基本忽略不计。
本文转自keakon的blog 原文地址:http://www.keakon.cn/bbs/thread-1858-1-1.html