3

我有一个包含 4,000 行的 .txt 文件,我正在尝试将它们插入 mysql,这里有两种方法可以做同样的事情,第一种方法很简单,编码如下:

$start = microtime(true);
foreach($b as $k=>$v){//$b is an array of 4,000 elements
    $db->exec("INSERT INTO siji (en,cn) VALUES ('$v[0]','$v[1]')");
}
echo microtime(true)-$start;//116 sec.

它需要 116 秒。第二种方法是使用 PDO::bindParam(),我知道对于重复的 SQL 查询,使用 bindparam() 是一个好习惯,因为每个查询之间的唯一区别是它们的值,所以我这样编码:

    $start = microtime(true);
$stmt = $db->prepare('INSERT INTO siji (en,cn) VALUES (:en,:cn)');
$stmt->bindParam(':en',$en);
$stmt->bindParam(':cn',$cn);
foreach($b as $k=>$v){//$b is an array of 4,000 elements
    $en = $v[0];
    $cn = $v[1];
    $stmt->execute();//
}
echo microtime(true)-$start;//127 sec.

第二种方法被认为比第一种更快,结果不是我想的那样,谁能告诉我 bindparam() 真的加速批量插入吗?或者使用 bindparam() 时可能有什么问题?

4

3 回答 3

1

你还没有指定你使用的数据库服务器,所以我假设 MySQL,因为它是最常见的。

直接回答您的问题:答案是肯定的,PDO 的prepare功能应该使用 DB 的 Prepared Statements 功能,这在运行类似这样的一批类似查询时应该会产生更快的结果。

然而,特别是对于 MySQL PDO 驱动程序,它默认模拟准备好的语句,而不是真正正确地使用它们。

这意味着默认情况下,在 PDO 对象内部,它基本上与您的第一个代码示例完全相同,即手动构建 SQL 字符串。

我不知道为什么这是默认行为(可能与旧的 mySQL 版本存在兼容性问题?),但为了防止它并强制 PDO 正确使用 Prepared Statements,您需要禁用此选项。

您可以按如下方式执行此操作:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

试试看,看看会发生什么。

顺便说一句,如果你的 .txt 文件有 4000 行,恰好是 CSV 或其他常规格式的文件,你可以使用 MySQL 的内置LOAD DATA INFILE函数,它可以通过单个查询将整个文件加载到数据库中。这总是在 PHP 中循环相同的查询 4000 次所能达到的速度快得多。(其他数据库具有类似的功能)。

于 2013-06-13T12:16:16.307 回答
0

我有一个包含 4,000 行的 .txt 文件,我正在尝试将它们插入 mysql

如果您担心速度,请使用LOAD DATA INFILE

此外,4000 次插入的 100 秒太长了。您必须将您的插入包装在事务中,或者考虑将您的 innodb 配置为不那么偏执的模式

于 2013-06-13T12:20:52.870 回答
0

第二种方法被认为比第一种更快,结果不是我想的那样,谁能告诉我 bindparam() 真的加快批量插入吗?

它实际上更快。只是不一定像您发布的那样琐碎查询。

这有点像对 MySQL 和 PostgreSQL 进行基准测试。如果您使用 MyISAM 表运行测试,该表执行微不足道的非并发选择,您的基准测试可能会确定 MySQL 优于 Postgres。但是,如果您使用六个连接运行数百个并发查询,您的基准测试可能会告诉您一个非常不同的故事。

在您的情况下,您正在准备一个简单的插入。解析 SQL 很简单;确定最佳查询计划同样简单。准备声明的好处非常渺茫。另一方面,如果您在每次插入时都有几个重要的触发器,那么您可能会得到一个非常不同的故事。

关于真正的准备与模拟的准备,还有一些话要说。有时,准备好的语句不会给你一个最佳计划。考虑这个查询:

select * from foo order by bar limit ?

如果您准备好上述内容,则计划者无法决定是否在 bar 上使用索引 - 如果 bar 足够低,那将是有意义的;如果它很大,您还不如获取整个表并对其进行 top-n 排序。所以计划者会选择后一个计划。

相反,如果您直接发送最终查询,规划器将拥有它需要的所有元素来决定使用相同的索引对于该特定值是否有意义。换句话说,模拟的准备有时更适合只运行一次的查询或琐碎的查询。

哦,别忘了把整个事情包装成一个事务。这将大大加快速度。

于 2013-06-13T12:22:26.693 回答