【问题标题】:Does php bindParam() speed up bulk insert?php bindParam() 是否加速批量插入?
【发布时间】:2013-06-13 20:22:26
【问题描述】:

我有一个包含 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() 真的可以加快批量插入吗?或者使用时可能有什么问题绑定参数()?

【问题讨论】:

标签: php pdo sqlbindparameter


【解决方案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 次所能达到的速度快很多。 (其他数据库也有类似的功能)。

【讨论】:

  • 我添加了 $db->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);但是执行时间没有显着变化,也许我必须尝试其他的东西,比如事务
【解决方案2】:

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

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

另外,4000 次插入的 100 秒实在是太长了。您必须将插入包装在事务中,或者考虑将您的 innodb 配置为 less paranoid mode

【讨论】:

    【解决方案3】:

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

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

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

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

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

    select * from foo order by bar limit ?
    

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-14
      • 1970-01-01
      • 1970-01-01
      • 2012-04-14
      • 2011-02-11
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多