我创建了一个小的Perl脚本来向表中插入大量条目 示例代码段:
$query = "insert into big_table (first_name, last_name) values (?, ?) ";
# prepare your statement for connecting to the database
$statement = $connection->prepare($query);
for(my $i = 1; $ i <= 70000; i++) {
my $first = "test".$i;
my $last = "test".$i;
$statement->execute($first, $last);
}
它插入了行,大约花了15分钟
但是当我这样做时:
CREATE TABLE big_table2 like big_table;
INSERT INTO big_table2 SELECT * FROM big_table;
只花了55秒!对于70000行
为什么会有如此巨大的差异?
答案 0 :(得分:6)
为了加快循环中的插入,您可以尝试使用事务:
将AutoCommit=>0
属性添加到连接中:
my $connection = DBI->connect($dsn,$username,$password, AutoCommit=>0); # transaction enabled
然后在for循环之后,您可以在单个原子操作中提交所有更改:
$connection->commit();
你注意到的性能差异是你在for循环中执行的几个操作,你正在执行一个查询并写入磁盘70000次,而另一个查询只有一次所有记录。
答案 1 :(得分:3)
也许是因为脚本必须写入磁盘70,000次,而INSERT SELECT最小化磁盘I / O.
此外,您可以简化SQL:[create table select syntax]
create table big_table2 select * from big_table;
我认为没有必要为SQL脚本中的内容编写Perl脚本。
答案 2 :(得分:2)
为什么会有这么大的差异?
因为必须将这些单独的INSERT语句(来自脚本)中的每一个都发送到数据库,在那里必须对其进行解析(检查语法和语义......关键字和标识符位于适当的位置,标识符是有效的,用户拥有对象的权限,...然后MySQL必须开发一个执行计划(可执行代码实际执行操作,插入它很简单,但仍然必须完成),然后MySQL必须执行操作(找到要修改的数据和索引块,获取必要的锁,进行所需的块更改,检查索引违规,触发触发器等,然后将块更改写入二进制/复制日志(二进制日志记录或语句日志记录)然后提交更改,释放锁,清理资源,并将状态返回给调用者。如果这是从远程计算机运行,那么通过网络往返数据库的那些往返会增加时间。
因此,每个语句都有开销。对于单个语句来说并不是很大,但是对于很多语句来说,它开始快速加起来(或加起来很慢)。
MySQL使用单个INSERT语句的工作量很少。
这就是为什么我们尽可能避免在循环RBAR(Row By Agonizing Row)中处理单个行的原因。
MySQL有一个优化,可以加速INSERT,在同一个语句中插入多行......
INSERT INTO mytable (mycol1, mycol2) VALUES ('a','a'),('b','b'),('c','c')
但是,在你对行数进行粗暴处理之前,SQL语句的最大大小是有限制的;我相信字节数受max_allowed_packet
的限制。 (请注意,限制是字节,而不是字符,以防您使用UTF-8而某些字符需要两个或更多字节。)
要在MySQL中生成70,000行而不需要脚本,您可以执行以下操作:
INSERT INTO mytable (first_name, last_name)
SELECT CONCAT('test',i.i) AS first_name
, CONCAT('test',i.i) AS last_name
FROM ( SELECT 1 + ten_thousands.digit*10000
+ thousands.digit*1000
+ hundreds.digit*100
+ tens.digit*10
+ ones.digit
AS i
FROM ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
) ten_thousands
CROSS
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) thousands
CROSS
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) hundreds
CROSS
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) tens
CROSS
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) ones
ORDER
BY ten_thousands.digit
, thousands.digit
, hundreds.digit
, tens.digit
, ones.digit
) i