2013-05-27 41 views
0
function generateRandomData(){ 
@ $db = new mysqli('localhost','XXX','XXX','scores'); 
if(mysqli_connect_errno()) { 
    echo 'Failed to connect to database. Please try again later.'; 
    exit; 
} 
$query = "insert into scoretable values(?,?,?)"; 
for($a = 0; $a < 1000000; $a++) 
{ 
$stmt = $db->prepare($query); 

$id = rand(1,75000); 

$score = rand(1,100000); 

$time = rand(1367038800 ,1369630800); 
$stmt->bind_param("iii",$id,$score,$time); 
$stmt->execute(); 
} 

} 

我想在一百万行数据的mysql中填充数据表。但是,这个过程非常缓慢。有什么明显的我做错了,我可以修复,以使其运行速度更快?优化插入数据到mysql

+0

我宁愿建议完全准备查询然后单次执行它。这将有效地减少时间 –

+0

在这里看到:http://stackoverflow.com/a/3711277/1755720 –

+0

没有预先准备好的声明没有注意到一个区别。我对Web开发很陌生,有一点我还不完全了解,那就是索引,这在我的表中是没有的。这可能会影响速度吗? – rfgordan

回答

1

正如在评论中暗示的那样,您需要通过连接尽可能多的插入来减少查询次数。在PHP中,很容易做到这一点:

$query = "insert into scoretable values"; 
for($a = 0; $a < 1000000; $a++) { 
    $id = rand(1,75000); 
    $score = rand(1,100000); 
    $time = rand(1367038800 ,1369630800); 
    $query .= "($id, $score, $time),"; 
} 
$query[strlen($query)-1]= ' '; 

上有查询的最大尺寸可以执行,这直接关系到max_allowed_packet服务器设置(MySQL文档的This page限制介绍如何调整该设置为您的优势)。因此,您必须减少上面的循环次数才能达到适当的查询大小,并通过用另一个循环包装该代码来重复该过程以达到要插入的总数。

另一种做法是对你希望做批量插入表中禁用检查约束:

ALTER TABLE yourtablename DISABLE KEYS; 
SET FOREIGN_KEY_CHECKS=0; 
-- bulk insert comes here 
SET FOREIGN_KEY_CHECKS=1; 
ALTER TABLE yourtablename ENABLE KEYS; 

这种做法然而,必须仔细,因为你生成的随机数值做,尤其是在你的情况。如果在生成的列中有任何唯一键,则不能在查询中使用该技术,因为它可能会生成重复的键插入。你可能想一个IGNORE条款添加到它:

$query = "insert INGORE into scoretable values"; 

这将导致服务器忽略这些独特的键重复的条目。为了达到所需插入物的总数,只需循环尽可能多的时间以填充剩余的缺失线。

我想唯一的地方,你可以有一个唯一的关键约束是在id列。在这种情况下,您将永远无法达到您想要的行数,因为它超出了为该字段生成的随机值范围。考虑提高这个限制,或者更好的是,以不同的方式生成你的ID(也许只需使用一个计数器,这将确保每个记录都使用不同的密钥)。

1

你正在做几件错事。首先你必须考虑的是你正在使用的MySQL引擎。

默认的是InnoDB,以前的默认引擎是MyISAM。

我会在假设你使用InnoDB的情况下写出这个答案,你应该使用这个答案的原因很多。 InnoDB在所谓的自动提交模式下运行。这意味着你所做的每个查询都包含在一个事务中。 要翻译成我们凡人能理解的语言 - 你做的每一个查询没有指定BEGIN WORK;块是一个事务 - ergo,MySQL将等待,直到硬盘确认数据已被写入。

知道硬盘驱动器速度很慢(机械驱动器仍然是最常用的驱动器),这意味着您的插入速度将与硬盘驱动器一样快。通常情况下,机械硬盘驱动器每秒可以执行大约300次输入输出操作,假设您可以每秒执行300次插入 - 是的,您将等待很长时间才能插入100万条记录。

因此,知道事情如何运作 - 您可以使用它们来获得优势。

HDD每笔交易写入的数据量通常会非常小(4KB甚至更少),并且知道今天的HDD可以写入超过100MB /秒 - 这表明我们应该将几个查询包装到单个事务中。

这样MySQL会发送相当多的数据,并等待硬盘确认它写了所有内容,整个世界都很好。

因此,假设您有1M行要填充 - 您将执行1M查询。如果您的事务一次提交1000个查询,则只应执行大约1000次写入操作。

这样一来,你的代码变得东西这样的:

(我不熟悉的mysqli接口,这样的函数名称可能是错的,看到我打字,而无需实际运行的代码 - 的例子可能不起作用请自行承担风险)

function generateRandomData() 
{ 
    $db = new mysqli('localhost','XXX','XXX','scores'); 

    if(mysqli_connect_errno()) { 
     echo 'Failed to connect to database. Please try again later.'; 
     exit; 
    } 

    $query = "insert into scoretable values(?,?,?)"; 

    // We prepare ONCE, that's the point of prepared statements 
    $stmt = $db->prepare($query); 

    $start = 0; 
    $top = 1000000; 

    for($a = $start; $a < $top; $a++) 
    { 
     // If this is the very first iteration, start the transaction 
     if($a == 0) 
     { 
      $db->begin_transaction(); 
     } 
     $id = rand(1,75000); 

     $score = rand(1,100000); 

     $time = rand(1367038800 ,1369630800); 

     $stmt->bind_param("iii",$id,$score,$time); 
     $stmt->execute(); 


     // Commit on every thousandth query 
     if(($a % 1000) == 0 && $a != ($top - 1)) 
     { 
      $db->commit(); 

      $db->begin_transaction(); 
     } 

     // If this is the very last query, then we just need to commit and end 
     if($a == ($top - 1)) 
     { 
      $db->commit(); 
     } 
    } 
} 
1

数据库查询涉及许多相关的任务。因此这是一个'昂贵'的过程。当涉及插入/更新时,它更加“昂贵”。

运行查询一次是提高性能的最佳方法。

您可以在循环中准备语句并运行一次。

例如。

$query = "insert into scoretable values "; 
for($a = 0; $a < 1000000; $a++) 
{ 
$values = " ('".$?."','".$?."','".$?."'), "; 
$query.=$values; 
... 


} 
... 
//remove the last comma 
... 
$stmt = $db->prepare($query); 
... 
$stmt->execute(); 
0

看看我创建的this gist。在笔记本电脑上插入一百万行需要大约5分钟的时间。