当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《Python有慢速数据库查询,但Perl没有》经验,为你挑选了2个好方法。

我使用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 - 更糟糕的结果.



1> Schwern..:

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

但是,看起来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

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


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

2> Jeff Burdges..:

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

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

推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有