Python有慢速数据库查询,但Perl没有

And*_*w G 12 python mysql database perl

我使用python(Django)作为我的网店.

当我测试高负载(db access)得到了有趣的结果:

python 10 process = 200sec / 100% CPU utilisation
perl 10 process  = 65sec / 35% CPU utilisation
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

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";
}
Run Code Online (Sandbox Code Playgroud)

红宝石中的代码:

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
Run Code Online (Sandbox Code Playgroud)

更新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%
Run Code Online (Sandbox Code Playgroud)

其他机器上的MySQL服务器.


更新3

尝试使用oursql和pymysql - 更糟糕的结果.

Sch*_*ern 10

正如人们所指出的那样,你在两者之间准备和执行陈述的方式并不相同,也不是推荐的做法.两者都应该利用准备好的陈述,两者都应该在循环之外做准备.

但是,看起来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)
Run Code Online (Sandbox Code Playgroud)

Perl代码

#!/usr/bin/perl
foreach $_ ( 1 .. 100000 )
{
 $random = int(rand(50000));
}
Run Code Online (Sandbox Code Playgroud)

Python时间

real    0m0.194s
user    0m0.184s
sys     0m0.008s
Run Code Online (Sandbox Code Playgroud)

Perl时间

real    0m0.019s
user    0m0.015s
sys     0m0.003s
Run Code Online (Sandbox Code Playgroud)

为了防止这在更敏感的基准测试中出现问题,请改为增加计数器.

  • @AndrewG这可能是OS X(我的)与Linux(你的)支持两种语言中的随机数的怪癖.你得到的数字并不是那么重要,但它可以摒弃一个基准. (2认同)

Jef*_*ges 9

理论上,如果$cursor = $db->prepare($sql);在循环之前执行并且只是重复执行相同的准备查询,则Perl代码应该显着加速.我怀疑DBI或MySQL只是缓存并忽略了您重复的相同查询准备.

另一方面,您的Python代码要求每次重新编译不同的查询,因为您没有使用准备好的查询.如果你在循环之前正确地准备两个查询,我会期望速度差异消失.顺便说一句,使用准备好的查询也有安全上的好处.