我有一个包含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()?
答案 0 :(得分:1)
你还没有指定你正在使用的数据库服务器,所以我会假设MySQL,因为它是最常见的。
直接回答你的问题:答案是肯定的,PDO的prepare
函数应该使用数据库的准备语句功能,这样可以在运行一批类似的查询时产生更快的结果。
然而,特别是MySQL PDO驱动程序,它默认模拟预准备语句而不是实际正确使用它们。
这意味着默认情况下,在PDO对象内部,它基本上与第一个代码示例完全相同,手动构建SQL字符串。
我不知道为什么这是默认行为(可能是旧的mySQL版本存在兼容性问题?),但要防止它并强制PDO正确使用Prepared Statements,您需要禁用此选项。 / p>
您可以按照以下方式执行此操作:
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
试试看,看看会发生什么。
顺便说一下,如果你的4000行的.txt文件碰巧是CSV或其他常规格式的文件,你可以使用MySQL的内置LOAD DATA INFILE
函数,它可以通过整个文件将整个文件加载到DB中单个查询。通过在PHP中循环相同的查询4000次,这总是远快于您可以实现的任何事情。 (其他DB具有类似的功能)。
答案 1 :(得分:0)
我有一个包含4,000行的.txt文件,我正在尝试将它们插入到mysql中
如果您担心速度
,请使用LOAD DATA INFILE此外,4000次插入100秒也是一种方式。您必须在事务中包装插入内容,或者考虑将innodb配置为less paranoid mode。
答案 2 :(得分:0)
第二种方法比第一种方法更快,结果不像我想的那样,有人能告诉我bindparam()真的加速批量插入吗?
实际上它更快。不一定是像你发布的那样琐碎的查询。
这有点像基准MySQL和PostgreSQL。如果您使用MyISAM表运行一个非常简单的非并发选择的测试,您的基准测试可能会认为MySQL优于Postgres。但是,如果您使用六个连接运行数百个并发查询,那么您的基准测试可能会告诉您一个完全不同的故事。
在你的情况下,你正在准备一个简单的插入。解析SQL是微不足道的;确定最佳查询计划同样微不足道。准备声明的好处非常渺茫。另一方面,如果你在每个插页上都有一些非平凡的触发器,你可能会得到一个非常不同的故事。
关于真实准备与模拟准备,还有一些事情要说。有时,准备好的声明不会给您一个最佳计划。请考虑以下问题:
select * from foo order by bar limit ?
如果您准备上述内容,规划人员无法决定是否使用条形索引 - 如果条形图足够低,则有意义;如果它是巨大的,你也可以获取整个表格并对其进行排序。因此,规划者将选择后一种计划。
相反,如果您直接发送最终查询,计划程序将拥有所需的所有元素来决定是否对该特定值使用相同的索引是否有意义。换句话说,对于仅运行一次的查询或者对于无关紧要的查询,模拟准备有时会更好。
哦,不要忘记将整个事情包装成一个事务。这会大大加快速度。