我使用Perl脚本从xml文件(包含超过10亿行)填充MySQL中的表,以查找感兴趣的行。该脚本运行非常顺利,直到第15M行,但之后它开始以指数方式增加一些。 就像第一个1000000行一样,需要大约12秒来解析并将其写入数据库,但是在15M行之后需要时间来解析和写入相同数量的行~43s。
我将innodb_buffer_pool_size从128M增加到1024M,如
所示Insertion speed slowdown as the table grows in mysql answered by Eric Holmberg
时间要求分别降至~7s和~32s,但由于我需要处理大量文件并且时间要求不断增加,因此时间要求仍然很慢。
此外,我删除了任何Primary key
和Index
的创建,认为它可能会导致一些问题(不确定)
以下是代码段:
$dbh = DBI->connect('dbi:mysql:dbname','user','password') or die "Connection Error: $DBI::errstr\n";
$stmt = "DROP TABLE IF EXISTS dbname";
$sth = $dbh->do($stmt);
$sql = "create table db(id INTEGER not null, type_entry VARCHAR(30) not null, entry VARCHAR(50))";
$sth = $dbh->prepare($sql);
$sth->execute or die "SQL Error: $DBI::errstr\n";
open my $fh1, '<', "file.xml" or die $!;
while (<$fh1>)
{
if ($_=~ m/some pattern/g)
{
$_=~ s/some pattern//gi;
$id = $_;
}
elsif ($_=~ m/some other pattern/)
{
$_=~ s/\s|(\some other pattern//gi;
$type = $_;
}
elsif ($_=~ m/still some other pattern/)
{
$_=~ s/still some other pattern//gi;
$entry = $_;
}
if($id ne "" && $type ne "" && $entry ne "")
{
$dbh->do('INSERT INTO dbname (id, type_entry, species) VALUES (?, ?, ?)', undef, $id, $type, $entry);
}
}
该数据库将包含约170万条目。还有什么办法可以减少时间?
先谢谢
谢谢大家的帮助 从早上起,我一直在努力实施已经告知的所有内容,并且正在检查我是否能够大幅减少时间。 所以我做了什么:
hash
(由@ikegami讲述)LOAD DATA LOCAL INFILE
(由@ikegami,@ ysth和@ThisSuitIsBlackNot讲述)。但我已将其嵌入到我的代码中以获取文件然后将其处理到数据库。此处的文件由脚本动态编写,当它达到1000个条目时,将写入db。连续1000000行的运行时间
13 s
11 s
24 s
22 s
35 s
34 s
47 s
45 s
58 s
57 s .....
(想发布图片但是......声誉)
我检查了时间并跟踪了脚本将其写入数据库所需的时间;令我惊讶的是它是线性的。现在我从这里得出的结论是,while循环存在一些问题,我相信它会以指数方式增加时间,因为它必须转到每次迭代的行号,并且当它深入到文件中时它必须计算更多的数字线到达下一行。
对此的任何评论
$start_time = time();
$line=0;
open my $fh1, '<', "file.xml" or die $!;
while (<$fh1>)
{
$line++;
%values;
if ($_=~ s/foo//gi)
{
$values{'id'} = $_;
}
elsif ($_=~ s/foo//gi)
{
$values{'type'} = $_;
}
elsif ($_=~ s/foo//gi)
{
$values{'pattern'} = $_;
}
if (keys(%values) == 3)
{
$no_lines++;
open FILE, ">>temp.txt" or die $!;
print FILE "$values{'id'}\t$values{'type'}\t$values{'pattern'}\n";
close FILE;
if ($no_lines == 1000)
{
#write it to database using `LOAD DATA LOCAL INFILE` and unlink the temp.txt file
}
undef %values;
}
if($line == ($line1+1000000))
{
$line1=$line;
$read_time = time();
$processing_time = $read_time - $start_time - $processing_time;
print "xml file parsed till line $line, time taken $processing_time sec\n";
}
}
首先,我要道歉,花了这么长时间才回复;当我从Perl的root到top再次开始时,这次use strict
显而易见,这有助于我保持线性时间。在处理大型Xml文件时,使用XML Parsers
也是一件好事。
而且要添加更多内容,MySQL插入的速度并不总是线性的
感谢大家的帮助和建议
答案 0 :(得分:1)
我猜测瓶颈是实际的插入。生成INSERT
语句,将它们放在一个文件中,然后使用mysql
命令行工具执行该文件肯定会快一点。
您可以尝试创建插入大量行与单个语句的INSERT
语句。
或者最好完全避免INSERT
语句。我认为mysql
命令行工具可以从CSV文件填充数据库。这可能会产生更快的速度。
更好的是,如果您有权访问托管数据库的计算机的文件系统,则可以使用LOAD DATA INFILE
。
您的Perl代码也可以使用一些清理工具。
您搜索每个模式两次?变化
if (/foo/) { s/foo//gi; $id = $_ }
到
if (s/foo//gi) { $id = $_ }
实际上,你需要替换吗?这可能会更快
if (/foo (.*)/) { $id = $1 }
看起来您可以按照
的方式做更多事情my ($k, $v) = split(/:\s*/);
$row{$k} = $v;
而不是那个巨人if
。
此外,如果您使用哈希,那么您可以使用以下内容进行最后检查:
if (keys(%row) == 3)