0

我创建了一个小的 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 行
,为什么会有如此巨大的差异?

4

3 回答 3

6

为了加快循环插入速度,您可以尝试使用事务:

AutoCommit=>0属性添加到连接:

my $connection = DBI->connect($dsn,$username,$password, AutoCommit=>0); # transaction enabled

然后在 for 循环之后,您可以在单个原子操作中提交所有更改:

$connection->commit();

您注意到的性能差异可归因于您在 for 循环中执行的几个操作,您正在执行一个查询并写入磁盘 70000 次,而另一个查询只有一次包含所有记录......

于 2013-08-14T20:42:39.453 回答
3

可能是因为脚本必须写入磁盘 70,000 次,而 INSERT SELECT 最小化了磁盘 I/O。

此外,您可以这样简化 SQL:[create table select syntax]

create table big_table2 select * from big_table;

我认为没有必要为您可以在 SQL 脚本中执行的操作编写 Perl 脚本。

于 2013-08-14T20:38:24.200 回答
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
于 2013-08-14T20:47:28.023 回答