如何让PHP运行更快?

3

我目前正在尝试将大约2000万行的数据从文本文件导入到表格中。速度太慢了(而且2000万行并不是全部内容)。

有没有什么方法可以加快这个过程?顺便提一下,我是从phpmyadmin运行的...

<?php 

//connection to the database
$dbhandle = mysql_connect($hostname, $username, $password) 
  or die("Unable to connect to MySQL");
echo "Connected to MySQL<br>";

$selected = mysql_select_db("data",$dbhandle) 
  or die("Could not select data");

$handle = fopen ('text.txt', 'rt');  
while (!feof ($handle))  
{  
    ini_set('max_execution_time',10800);
    $buffer = fgets($handle, 4096);

    list($a,$b,$c)=explode(" ",$buffer);
    $lol = explode(".",$c);
    $rest = substr($c,-5,3);
    $date = date('Y-m-d H:i:s');


    if($rest == 'COM'){
    echo $a." | ".$b." | ".$c."<br>";
    $sqlquery = "INSERT INTO zonenet (date, domainname, dnstype, nameserver) VALUES('".$date."','".$a."','".$b."','".$c."')";   
    mysql_query($sqlquery,$dbhandle) or die(mysql_error());
    } 
    else {
    //$dnstype = array("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ".",
//          "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
    $str = str_replace($lol[1],".NET",$lol[1]);
    //$str = str_replace(end($lol),".NET",end($lol));
    echo $a." | ".$b." | ".$lol[0]."".$str."<br>";
    $sqlquery = "INSERT INTO zonenet (date, domainname, dnstype, nameserver) VALUES('".$date."','".$a."','".$b."','".$lol[0]."".$str."')";  
    mysql_query($sqlquery,$dbhandle) or die(mysql_error());
    }
    //dnstype reference @http://en.wikipeda.org/wiki/List_of_DNS_record_types
    $sqlquery1 = "DELETE FROM zonenet WHERE dnstype NOT IN ('A', 'AAAA', 'AFSDB', 'APL', 'CAA', 'CERT', 'CNAME', 'DHCID'`enter code here`, 'DLV', 'DNAME', 'DNSKEY', 'DS', 'HIP', 'IPSECKEY', 'KEY', 'KX', 'LOC', 'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'PTR', 'RRSIG', 'RP', 'SIG', 'SOA', 'SPF', 'SRV', 'SSHFP', 'TA', 'TKEY', 'TLSA', 'TSIG', 'TXT')"; 
        mysql_query($sqlquery1,$dbhandle) or die(mysql_error()); 

}   
fclose ($handle); 

//close the connection
mysql_close($dbhandle);

    //$net=explode(".",$c);
    //echo $a."-".$b."-".$c."<br>";
    //

    //
    //mysql_query($sqlquery,$dbhandle) or die(mysql_error()); 
?> 

2
你目前实现了什么?你尝试过什么? - Michael Sivolobov
我刚刚编辑了我的帖子,是的,当导入大量数据时它会崩溃很多次。 - Amaya IceChild Nestaz
2个回答

1

2000万条记录并不算太多,所以这应该很容易进行优化:

  1. 在循环中不需要使用ini_set,只需要一次即可。
  2. 使用批量插入。现在你为每个记录都执行一个插入操作。
  3. 在插入之前进行处理检查,而不是“删除”错误的记录。

代码已更改(未经测试):

<?php

//connection to the database
$dbhandle = mysql_connect($hostname, $username, $password) or die("Unable to connect to MySQL");

echo "Connected to MySQL<br>";

$selected = mysql_select_db("data",$dbhandle) or die("Could not select data");

ini_set('max_execution_time',10800);

$allowedDnsType = array('A', 'AAAA', 'AFSDB', 'APL', 'CAA', 'CERT', 'CNAME', 'DHCID', 'DLV', 'DNAME', 'DNSKEY', 'DS', 'HIP', 'IPSECKEY', 'KEY', 'KX', 'LOC', 'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'PTR', 'RRSIG', 'RP', 'SIG', 'SOA', 'SPF', 'SRV', 'SSHFP', 'TA', 'TKEY', 'TLSA', 'TSIG', 'TXT');
$lookup = array_flip($allowedDnsType);

$values = array();

$handle = fopen ('text.txt', 'rt');
while (!feof ($handle)) {
    $buffer = fgets($handle, 4096);

    list($domain,$dnstype,$nameserver)=explode(" ",$buffer);

    $lol = explode(".",$nameserver);
    $rest = substr($nameserver,-5,3);
    $date = date('Y-m-d H:i:s');


    if (isset($lookup[$dnstype])) {

        if($rest == 'COM'){
            echo $domain." | ".$dnstype." | ".$c."<br>";
            $values[] = "'".$date."','".$domain."','".$dnstype."','".$nameserver."'";
        }
        else {
            $str = str_replace($lol[1],".NET",$lol[1]);
            //$str = str_replace(end($lol),".NET",end($lol));
            echo $domain." | ".$dnstype." | ".$lol[0]."".$str."<br>";
            $values[] = "'".$date."','".$domain."','".$dnstype."','".$lol[0].$str."'";
        }
    }

    // insert per 200
    if (count($values) > 200) {
        $sqlquery = "INSERT INTO zonenet (date, domainname, dnstype, nameserver) VALUES(".implode('),(', $values).")";
        mysql_query($sqlquery,$dbhandle) or die(mysql_error());
        $values = array();
    }

    //dnstype reference @http://en.wikipeda.org/wiki/List_of_DNS_record_types
}
fclose ($handle);

if (count($values) > 0) {
    $sqlquery = "INSERT INTO zonenet (date, domainname, dnstype, nameserver) VALUES(".implode('),(', $values).")";
    mysql_query($sqlquery,$dbhandle) or die(mysql_error());
}

//close the connection
mysql_close($dbhandle);

0

2000万行需要时间,因为磁盘限制了在mysql中实际可以插入的数量。

我对此的最佳建议是使用预处理语句。根据您的mysql驱动程序,它可能是这样的:

$driver->prepareStatement("insert into table (`field`) values (?)");
foreach ($rows as $row) {
   $driver->bindParam('s', $row->value);
   $driver->execute();
}

根据服务器上的内存情况,以上代码可能会崩溃,因为所有行都一次性加载到内存中。可以通过逐行流式传输文件来解决这个问题,但是使用像http://php.net/manual/en/function.stream-get-line.php这样的函数应该相当简单。


6
不要使用这种方法。让数据库发挥其设计的最佳性能。使用MySQL命令LOAD DATA INFILE,速度会快得多。 - Mark
1
根据您想要实现的目标,我同意。如果您只是想直接导入大量数据,则Load data方法或mysqlimport是更好的解决方案。但是,这个问题被标记为“PHP”,因此我假设在导入过程中可能最终会进行一些PHP处理。 - user2849406
1
直接从PHP级别对文件进行处理,然后将其直接导入MySQL仍然会更快。 - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接