处理大文件时的AWK性能



我有一个awk脚本,用于计算一些事务完成所需的时间。脚本获取每个事务的唯一ID,并存储每个事务的最小和最大时间戳。然后它计算差值,最后显示超过60秒的结果。

当与数千(20万)人一起使用时,它效果很好,但在现实世界中使用时需要更多的时间。我对它进行了几次测试,处理大约2800万条线路大约需要15分钟。我能认为这是一个好的表现吗?还是有可能改进它?

我对任何建议都持开放态度。

这里有完整的代码

zgrep -E "(([a-z0-9]){15,}:" /path/to/very/big/log |  awk '{
gsub("[()]|:.*","",$4); #just removing ugly chars
++cont
min=$4"min" #name for maximun value of current transaction
max=$4"max" #same as previous, just for readability 
split($2,secs,/[:,]/) #split hours,minutes and seconds
seconds = 3600*secs[1] + 60*secs[2] + secs[3] #turn everything into seconds
if(arr[min] > seconds || arr[min] == 0)
  arr[min]=seconds
if(arr[max] < seconds)
   arr[max]=seconds
dif=arr[max] - arr[min]
if(dif > 60)
  result[$4] = dif
}
END{
for(x in result)
   print x" - "result[x]
print ":Processed "cont" lines"
}'

您不需要每次读取记录时都计算dif。只需在END部分中执行一次即可。

您不需要那个cont变量,只需使用NR.即可

您不需要分别填充最小值和最大值。字符串串联在awk中很慢。

您不应该更改$4,因为这将强制重新编译记录。

试试这个:

awk '{
    name = $4
    gsub(/[()]|:.*/,"",name); #just removing ugly chars
    split($2,secs,/[:,]/) #split hours,minutes and seconds
    seconds = 3600*secs[1] + 60*secs[2] + secs[3] #turn everything into seconds
    if (NR==1) {
        min[name] = max[name] = seconds
    }
    else {
        if (min[name] > seconds) {
            min[name] = seconds
        }
        if (max[name] < seconds) {
            max[name] = seconds
        }
    }
}
END {
    for (name in min) {
        diff = max[name] - min[name]
        if (diff > 60) {
            print name, "-", diff
        }
    }
    print ":Processed", NR, "lines"
}'

经过一些测试,并根据Ed Morton给出的建议(包括代码改进和性能测试),我发现瓶颈是zgrep命令。下面是一个做了几件事的例子:

  • 检查我们是否有交易行(第一个如果)
  • 清除事务id
  • 通过检查它是否在数组中来检查它是否已经注册(第二个if)
  • 如果未注册,则检查它是否是合适的事务类型,如果是,则以秒为单位注册时间戳
  • 如果已注册,则将新时间戳保存为最大值
  • 毕竟,它进行了必要的运算来计算时差

非常感谢所有帮助我的人。

zcat /veryBigLog.gz |  awk '
{if($4 ~ /^([:alnum:]/ ){
    name=$4;gsub(/[()]|:.*/,"",name);
    if(!(name in min)){
        if($0 ~ /TypeOFTransaction/ ){
          split($2,secs,/[:,]/) 
          seconds = 3600*secs[1] + 60*secs[2] + secs[3] 
          max[name] = min[name]=seconds
          print lengt(min) "new "name " start at "seconds
       }
       }else{ 
            split($2,secs,/[:,]/)
            seconds = 3600*secs[1] + 60*secs[2] + secs[3]
            if( max[name] < seconds) max[name]=seconds
            print name " new max " max[name]
        }
        }}END{
            for(x in min){
                dif=max[x]- min[x]
                print max[x]" max - min "min[x]" : "dif
            }
            print "Processed "NR" Records"
            print "Found "length(min)" MOs" }' 

最新更新