PHP MySQL尽可能快速有效地插入1-3,000行

时间:2012-03-07 04:57:25

标签: php mysql performance

我正在寻找使用PHP将INSERT 1-3,000行放入MySQL数据库的最快方法。我目前的解决方案是花费大约42秒来插入我认为可能更快的行。

我使用的是自编的DB类,insert()方法需要两个参数(string) $table(array) $vars$items数组是一个关联数组,其中键是表中的列名,值是要插入的值。这非常有效,因为我有时在一个表中有30列,并且已经在数组中有数据。 insert()方法如下:

    function insert($table,$vars) {
        if(empty($this->sql_link)){
            $this->connection();
        }
        $cols = array();
        $vals = array();
        foreach($vars as $key => $value) {
            $cols[] = "`" . $key . "`";
            $vals[] = "'" . $this->esc($value) . "'";
        }
        //join the columns and values to insert into sql
        $fields = join(', ', $cols);
        $values = join(', ', $vals);

        $insert = mysql_query("INSERT INTO `$table` ($fields) VALUES ($values);", $this->sql_link);
        return $insert;
}

它应该是不言自明的,但基本上我从$ vars中获取键和值并创建一个INSERT语句。它有效,我认为我遇到的问题是一次发送一个查询。

我应该构建一个长查询字符串吗?

INSERT INTO table (field, field2, etc) VALUES (1, 2, ect);INSERT INTO table (field, field2, etc) VALUES (1, 2, ect);INSERT INTO table (field, field2, etc) VALUES (1, 2, ect);INSERT INTO table (field, field2, etc) VALUES (1, 2, ect);INSERT INTO table (field, field2, etc) VALUES (1, 2, ect);并一次发送所有内容?如果是这样,这可以在一次调用中处理3,000个插入语句吗?

我还有另一种不看的方式吗?任何信息都表示赞赏。

由于

6 个答案:

答案 0 :(得分:12)

最高效的方法是使用多行插入语法:

INSERT INTO table (field, field2, etc) VALUES (1, 2, etc),(1, 2, etc),(1, 2, etc);

Manual

  

使用VALUES语法的INSERT语句可以插入多行。为此,请包含多个列值列表,每个列值都括在括号内并用逗号分隔。例如:

     

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

     

每行的值列表必须括在括号内。

答案 1 :(得分:6)

提高插入速度的两种方法:

  1. 一开始,在任何INSERT之前,请执行mysql_query("START TRANSACTION");或更简单的mysql_query("BEGIN");。最后,做一个mysql_query("COMMIT");。这两条线可以加快批量插入的速度,达到5-10倍的性能。

  2. 如果表后端为MyISAM(不是InnoDB),请执行INSERT后跟单词DELAYED。例如,代替INSERT INTO table使用INSERT DELAYED INTO table进行10-15倍的加速。

  3. 如果结合使用这两种方法,可以实现100倍的加速。

答案 2 :(得分:1)

Mysql可以直接从文件导入数据,这可以显着加快导入数据的速度。参见:

LOAD DATA INFILE Syntax

答案 3 :(得分:0)

像往常一样,这取决于;你甚至没有提到你正在使用哪种引擎,这是一个重要的决定因素。但我发现MySQL手册指南非常可靠。

http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

答案 4 :(得分:0)

自动发现插入的最大数量。

插入那种ammounts(3000)应该没有任何问题(假设你使用pdo):

$stmt = $dbh->prepare("INSERT INTO yourtable(name, id) VALUES " . str_repeat('(?,?),', $amountOfRows - 1) . '(?,?)');

您可以改进这一点,以创建通用的方式来创建大型语句,如上面的表格,用于具有不同字段的表:

$fields = array("name", "id");
$fieldList = implode(", ", $fields);
$params = '(' . str_repeat('?,', count($fields) - 1) . '?)';
$values = str_repeat($params . ',', $ammountOfRows - 1) .  $params;
$stmt = $dbh->prepare("INSERT INTO $table($fieldList) VALUES " . $values);

但上述解决方案的问题是不能使用任何行和字段组合。

似乎mysql不仅受到行数的限制,而且还考虑了大量的参数。

但是,每当新的mysql版本更改参数,行或甚至sql句子的大小时,您都不希望更改代码。

因此,创建一种生成大型语句的通用方法的更好方法是尝试改进底层数据库引擎:

/**
 * Creates an insert sql with the maximum allowed of parameters
 * @param string $table
 * @param string $attributeList
 * @param int &$ammountInserts returns the ammount of inserts
 * @return \PDOStatement
 */
public static function getBiggestInsertStatement($table, $attributeList, $max, &$ammountInserts)
{
    $previousSize = null;
    $size = 10;
    $sql = 'INSERT INTO ' . $table . '(' . implode(',', $attributeList) . ') values ';
    $return = null;
    $params = '(' . str_repeat('?,', count($attributeList) - 1) . '?)';

    do {
        try {
            $previousSize = $size;
            $values = str_repeat($params . ',', $size - 1) .  $params;
            $return = Db::getInstance()->prepare($sql . $values);
            if ($size > $max) {
                $values = str_repeat($params . ',', $max - 1) .  $params;
                $return = Db::getInstance()->prepare($sql . $values);
                $ammountInserts = $max;
                break;
            }
            $ammountInserts = $size;
            $size *= 2;
        } catch(\Exception $e) {

        }
    } while($previousSize != $size);

    return $return;
}

你必须记住的一件事是,既然你不知道这些限制,那么查询就能够推动你需要插入的所有项目的较低数量。

因此,您必须创建一个类似下面的策略,以便在任何可能的情况下成功地实现所有策略:

    $insert = Db::getBiggestInsertStatement($table, array('field1','field2'), $numrows, $maximumInserts);
    $i = 0;
    $values = array();
    for ($j = 0; $j < $numrows; $j++) {
        if ($i === $maximumInserts) {
            $insert->execute($values);
            $i = 0;
            $values = array();
        }
        $values[] = "value1" . $j;
        $values[] = "value2" . $j;
        $i++;
    });
    if ($i > 0) {
        $insertRemaining = Db::getBiggestInsertStatement($table, array('field1', 'field2'), $i, $maximumInserts);
        $insertRemaining->execute($values);
    }

我试图在一个包含单列1000000行的表中插入,并且在几秒钟内完成,再过几分钟就可以逐个插入它们。

答案 5 :(得分:-1)

加速批量插入的标准技术,在事务内部的循环内使用准备好的SQL语句。这将使它非常优秀。之后你可以尝试以各种方式调整它,但你可能会浪费你的时间。