我正在尝试使用Perl将大文件中的数据合并到组合文件中。
文件将处于打开状态,并且不断向文件中添加大量数据。每分钟追加约50,000行。
文件存储在由10到30台机器访问的网络共享文件夹中。
这些是JMeter生成的JTL文件。
此合并每分钟运行约6或7小时,所用时间不应超过30至40秒。
该进程由Linux机器中部署的Web应用程序每分钟触发一次。
我编写了一个脚本,将单个文件添加的最后一行存储在单独文件中的合并文件中。
这可以在15分钟内正常工作,但不断增加合并时间。
#!/usr/bin/perl
use File::Basename;
use File::Path;
$consolidatedFile = $ARGV[0];
$testEndTimestamp = $ARGV[1];
@csvFiles = @ARGV[ 2 .. $#ARGV ];
$testInProcess = 0;
$newMerge = 0;
$lastLines = "_LASTLINES";
$lastLine = "_LASTLINE";
# time() gives current time timestamp
if ( time() <= $testEndTimestamp ) {
$testInProcess = 1;
}
# File exists, has a size of zero
if ( -z $consolidatedFile ) {
mkdir $consolidatedFile . $lastLines;
$newMerge = 1;
}
open( CONSOLIDATED, ">>" . $consolidatedFile );
foreach my $file ( @csvFiles ) {
open( INPUT, "<" . $file );
@linesArray = <INPUT>;
close INPUT;
if ( $newMerge ) {
print CONSOLIDATED @linesArray[ 0 .. $#linesArray - 1 ];
open my $fh, ">", $consolidatedFile . $lastLines . "/" . basename $file . $lastLine;
print $fh $linesArray[ $#linesArray - 1 ];
close $fh;
}
else {
open( AVAILABLEFILE, "<" . $consolidatedFile . $lastLines . "/" . basename $file . $lastLine );
@lineArray = <AVAILABLEFILE>;
close AVAILABLEFILE;
$availableLastLine = $lineArray[0];
open( FILE, "<" . $file );
while ( <FILE> ) {
if ( /$availableLastLine/ ) {
last;
}
}
@grabbed = <FILE>;
close( FILE );
if ( $testInProcess ) {
if ( $#grabbed > 0 ) {
pop @grabbed;
print CONSOLIDATED @grabbed;
open( AVAILABLEFILE, ">" . $consolidatedFile . $lastLines . "/" . basename $file . $lastLine );
print AVAILABLEFILE $grabbed[ $#grabbed - 1 ];
}
close AVAILABLEFILE;
}
else {
if ( $#grabbed >= 0 ) {
print CONSOLIDATED @grabbed;
}
}
}
}
close CONSOLIDATED;
if ( !$testInProcess ) {
rmtree $consolidatedFile . $lastLines;
}
我需要优化脚本以减少时间。
是否可以将最后一行存储在缓存中?
有人可以为这种类型的合并提出另一种方法吗?
另一个脚本,它将最后一行存储在缓存中而不是文件中。
即使这样也不会在1分钟内完成合并。
#!/usr/bin/perl
use CHI;
use File::Basename;
use File::Path;
my $cache = CHI->new(
driver => 'File',
root_dir => '/path/to/root'
);
$consolidatedFile = $ARGV[0];
$testEndTimestamp = $ARGV[1];
@csvFiles = @ARGV[ 2 .. $#ARGV ];
$testInProcess = 0;
$newMerge = 0;
$lastLines = "_LASTLINES";
$lastLine = "_LASTLINE";
# time() gives current time timestamp
if ( time() <= $testEndTimestamp ) {
$testInProcess = 1;
}
# File exists, has a size of zero
if ( -z $consolidatedFile ) {
$newMerge = 1;
}
open( CONSOLIDATED, ">>" . $consolidatedFile );
foreach my $file (@csvFiles) {
$fileLastLineKey =
$consolidatedFile . $lastLines . "_" . basename $file . $lastLine;
open( INPUT, "<" . $file );
@linesArray = <INPUT>;
close INPUT;
if ($newMerge) {
print CONSOLIDATED @linesArray[ 0 .. $#linesArray - 1 ];
$fileLastLine = $linesArray[ $#linesArray - 1 ];
$cache->set( $fileLastLineKey, $fileLastLine );
}
else {
$availableLastLine = $cache->get($fileLastLineKey);
open( FILE, "<" . $file );
while (<FILE>) {
if (/$availableLastLine/) {
last;
}
}
@grabbed = <FILE>;
close(FILE);
if ($testInProcess) {
if ( $#grabbed > 0 ) {
pop @grabbed;
print CONSOLIDATED @grabbed;
$fileLastLine = $grabbed[ $#grabbed - 1 ];
$cache->set( $fileLastLineKey, $fileLastLine );
}
}
else {
if ( $#grabbed >= 0 ) {
print CONSOLIDATED @grabbed;
$cache->remove($fileLastLineKey);
}
}
}
}
close CONSOLIDATED;
我正在考虑将文件从最后一行读取到所需行,并将这些行复制到合并文件中。
有人可以建议吗???
答案 0 :(得分:0)
您可能想尝试在binmode中打开文件并在循环中以块状方式读取它。这通常可以显着提高性能。以下函数是一个示例,这里我将数组中文件的最大$ maxblocks块放在作为引用传递的数组中的块$ offset on上。请注意,当文件不够大时,最后一个块可能不包含整个$ block字节。
sub file2binarray {
my $file=shift;
my $array=shift;
my $maxblocks=shift;
my $offset=shift;
my $block=2048;
$offset=0 if ((!defined($offset)) || ($offset !~/^\s*\d+\s*$/o));
$maxblocks="ALL"
if (!defined($maxblocks) || ($maxblocks!~/^\s*\d+\s*$/o));
my $size=(stat($file))[7];
my $mb=$size/$block;
$mb++ if ($mb*$block<$size);
$maxblocks=$mb-$offset if(($maxblocks eq "ALL")||
($maxblocks>$mb-$offset));
$offset*=$block;
open(IN,"$file") || die("Cannot open file <$file>\n");
binmode(IN);
$bytes_read=$block;
seek(IN,$offset,0);
my ($blk,$bytes_read,$buffer)=(0,0,"");
while (($bytes_read==$block)&& ($blk<$maxblocks)){
$bytes_read=sysread(IN,$buffer,$block);
push(@$array,$buffer);
$blk++;
}
close(IN);
}
以1来读取整个文件,例如你这么称呼它
my @array;
my $filename="somefile";
file2binarray ($filename,\@array,"ALL",0);
但是你可能宁愿在循环中调用它,并在偏移量上记录一些,并在后续调用之间解析数组。 希望这会有所帮助。