为超大文件(60GB)使用Perl脚本提高MySQL中插入表的速度
我使用Perl脚本从一个xml文件(包含超过10亿行)填充MySQL中的一个表,以查找感兴趣的行。脚本运行非常平稳,直到第15M行,但在这之后,它开始以指数方式增加一些内容。 与前1000000行一样,解析并写入数据库需要12秒,但在15万行之后,解析和写入相同数量的行需要43秒 我将innodb_buffer_pool_大小从128M增加到了1024M,正如在 时间需求分别下降到~7秒和~32秒,但仍然很慢,因为我有一个巨大的文件要处理,而且它的时间需求不断增加 此外,我还删除了任何为超大文件(60GB)使用Perl脚本提高MySQL中插入表的速度,mysql,perl,Mysql,Perl,我使用Perl脚本从一个xml文件(包含超过10亿行)填充MySQL中的一个表,以查找感兴趣的行。脚本运行非常平稳,直到第15M行,但在这之后,它开始以指数方式增加一些内容。 与前1000000行一样,解析并写入数据库需要12秒,但在15万行之后,解析和写入相同数量的行需要43秒 我将innodb_buffer_pool_大小从128M增加到了1024M,正如在 时间需求分别下降到~7秒和~32秒,但仍然很慢,因为我有一个巨大的文件要处理,而且它的时间需求不断增加 此外,我还删除了任何主键和索
主键
和索引
的创建,我认为这可能会导致一些问题(尽管不确定)
下面是代码片段:
$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);
}
}
答复:
首先,我很抱歉花了这么长时间回复;当我再次从Perl的根开始到顶部时,这次我清楚地看到了使用strict
,这有助于我保持线性时间。在处理大型XML文件时,使用XML解析器也是一件好事
更重要的是,MySQL的插入速度与此无关,它始终是线性的
谢谢大家的帮助和建议我猜瓶颈在于实际的插入。生成INSERT
语句,将它们放在一个文件中,然后使用mysql
命令行工具执行该文件肯定会快一点
您可以尝试创建插入大量行的INSERT
语句,而不是单个语句
或者最好完全避免INSERT
语句。我认为mysql
命令行工具具有从CSV文件填充数据库的功能。这可能会提高一点速度
更好的是,如果您可以访问托管数据库的计算机的文件系统,则可以使用LOAD DATA infle
您的Perl代码也可以进行一些清理
- 每种图案都搜索两次?改变
if (/foo/) { s/foo//gi; $id = $_ }
到
- 事实上,你需要替换吗?这可能更快
if (/foo (.*)/) { $id = $1 }
- 看起来你可能可以做更多的事情
my ($k, $v) = split(/:\s*/);
$row{$k} = $v;
而不是那个巨大的if
- 此外,如果使用哈希,则可以在最后一次检查中使用以下内容:
if (keys(%row) == 3)
所有mysql表都有一个主键;如果您没有,mysql会为您添加一个隐藏的。因此,如果您有一个字段可以用作主键,那么最好显式地将其设置为主键?你这里的东西真的没有意义;看起来您将跳过一些初始行(其中未设置三个变量中的一个),但随后会为每一行写入…如果id是主键,则更改条目或类型的行将因重复的主键而导致插入失败命令行工具与其他任何工具一样只是一个客户端,所以它的插入速度不会更快。您可以使用csv导入命令通过它或仅在脚本中加载数据。@ysth,当然可以。减少开销。没有标量创建,etcLOAD数据有一个本地选项使客户端读取文件。这两种风格都可能有点难以工作(正确设置mysql权限/设置或服务器上的安全强化)。批处理插入(让每个insert语句插入数千行)几乎可以为您带来同样多的好处。@ysth根据我的经验,LOAD DATA infle
在某个阈值之后比批处理插入快得多(在我进行基准测试的应用程序中,大约100行,但每个应用程序的速度会有所不同)。我没有将它与LOCAL
一起使用,这样会比较慢,但确实需要一个基准测试。不要忘记,批处理插入的最大大小受max\u allowed\u packet
的限制,该数据包最多只能达到1 GB。@ThisSuitesBlack否:是。但两者都比单个插入快得多;我怀疑在这种情况下,仅仅批量处理插入内容就足够快了。
my ($k, $v) = split(/:\s*/);
$row{$k} = $v;
if (keys(%row) == 3)