我使用python(Django)作为我的网店.
当我测试高负载(db access)得到了有趣的结果:
python 10 process = 200sec / 100% CPU utilisation perl 10 process = 65sec / 35% CPU utilisation
Centos 6,python 2.6,mysql 5.5,标准库,其他服务器上的mysql-server.表product_cars有700万条记录.
为什么python程序这么慢?
Python程序:
#!/usr/bin/python import MySQLdb import re from MySQLdb import cursors import shutil import datetime import random db0 = MySQLdb.connect(user="X", passwd="X", db="parts") cursor0 = db0.cursor() cursor0.execute('SET NAMES utf8') now = datetime.datetime.now() for x in xrange(1, 100000): id = random.randint(10, 50000) cursor0.execute("SELECT * FROM product_cars WHERE car_id=%s LIMIT 500", [id]) cursor0.fetchone()
Perl程序:
#!/usr/bin/perl use DBI; my $INSTANCE=$ARGV[0]; my $user = "x"; my $pw = "x"; my $db = DBI->connect( "dbi:mysql:parts", "x", "x"); my $sql= "SELECT * FROM product_cars WHERE car_id=? LIMIT 500"; foreach $_ ( 1 .. 100000 ) { $random = int(rand(50000)); $cursor = $db->prepare($sql); $cursor->execute($random) || die $cursor->errstr; @Data= $cursor->fetchrow_array(); } $cursor->finish; $db->disconnect;
UPDATE1
有趣的事情:
选择始终为id = 1的行:
Сlear认为MYSQL使用缓存和查询会非常快,但再次缓慢且100%的CPU利用率.但是相同的perl或ruby代码工作得很快.
如果在python代码中替换字符串:
# remove "SET NAMES utf8" string - this has no impact # python-mysql use "%s", but not "?" as parameter marker id = 1 for x in xrange(1, 100000): id = 1 cursor0.execute("SELECT * FROM product_cars WHERE car_id=%s LIMIT 500", [id]) cursor0.fetchone()
perl中的代码相同:
foreach $_ ( 1 .. 20000 ) { $cursor = $db->prepare( "SELECT * FROM product_cars WHERE car_id=? LIMIT 500";); $cursor->execute(1); # while (my @Data= $cursor->fetchrow_array()) if ($_ % 1000 == 0) { print "$_\n" };. @Data= $cursor->fetchrow_array(); # print "$_\n"; }
红宝石中的代码:
pk=2 20000.times do |i| if i % 1000 == 0 print i, "\n" end res = my.query("SELECT * FROM product_cars WHERE car_id='#{pk}' LIMIT 500") res.fetch_row end
更新2
Exec SQL "SELECT * FROM product WHERE id=1" (string without params) 100000 times Python: ~15 sec 100% CPU 100% Perl: ~9 sec CPU 70-90% Ruby: ~6 sec CPU 60-80%
其他机器上的MySQL服务器.
更新3
尝试使用oursql和pymysql - 更糟糕的结果.
正如人们所指出的那样,你在两者之间准备和执行陈述的方式并不相同,也不是推荐的做法.两者都应该利用准备好的陈述,两者都应该在循环之外做准备.
但是,看起来Python MySQL驱动程序根本没有利用服务器端预处理语句.这可能是表现不佳的原因.
在MySQL 4.1中添加了服务器端预处理语句,但是一些驱动程序的适应速度很慢.该MySQLdb的用户指南只字不提准备的语句,认为"有在MySQL没有光标,没有参数替代",这从MySQL 4.1一直没有实现.它还说"MySQLdb的Connection和Cursor对象是用Python编写的",而不是利用MySQL API.
您可能想看看oursql 驱动程序.看起来它是为了利用"新的"MySQL API而编写的,让数据库自我优化.
DBD :: mysql(Perl MySQL驱动程序)可以利用预处理语句,但默认情况下根据文档不行.您必须通过添加mysql_server_prepare=1
到您的dsn 来打开它.这应该使Perl示例运行得更快.或者文档是撒谎,默认情况下它们处于打开状态.
顺便说一句,有一件事会抛弃基准,虽然没有考虑到2分钟的差异,但却产生了随机数.它们的成本很高.
Python代码
#!/usr/bin/python import random for x in xrange(1, 100000): id = random.randint(0, 50000)
Perl代码
#!/usr/bin/perl foreach $_ ( 1 .. 100000 ) { $random = int(rand(50000)); }
Python时间
real 0m0.194s user 0m0.184s sys 0m0.008s
Perl时间
real 0m0.019s user 0m0.015s sys 0m0.003s
为了防止这在更敏感的基准测试中出现问题,请改为增加计数器.
理论上,如果$cursor = $db->prepare($sql);
在循环之前执行并且只是重复执行相同的准备查询,则Perl代码应该显着加速.我怀疑DBI或MySQL只是缓存并忽略了您重复的相同查询准备.
另一方面,您的Python代码要求每次重新编译不同的查询,因为您没有使用准备好的查询.如果你在循环之前正确地准备两个查询,我会期望速度差异消失.顺便说一句,使用准备好的查询也有安全上的好处.