高效的前 10 排序多列

Efficient top 10 sort of multiple columns

寻找一种有效的方法来从 Linux 服务器上的大型数据集中获取多个数字字段的前 3 个(可扩展);这是 的后续内容,其中对

的最佳建议

"I have an awk array that aggregates bytes up and downloaded. I can sort the output by either bytes down or up and pipe that to head for the top talkers; is it possible to output two sorts using different keys?"

是:

zgrep '^1' 20211014T00*.gz|
awk '
    NR > 1 {
         key =  " " 
         bytesdown[key] += 
         bytesup[key] += 
     }
     END {
         cmd = "sort -rn | head -3"
         for ( key in bytesDown ) {
             print bytesDown[key], bytesUp[key], key | cmd
         }
         close(cmd)
 
         cmd = "sort -rnk2 | head -3"
         for ( key in bytesDown ) {
             print bytesDown[key], bytesUp[key], key | cmd
         }
         close(cmd)
     }
'

但是,由于数据集的范围可以从 1000 行到数百万行,而不是将整个集合读入数组,排序并丢弃绝大多数,维护前 10 行的数组是否可行数据读入?绝对速度比内存消耗更不是问题,内存消耗是服务器上相对有限的资源。

例如,给定以下示例输入:

ip1     fqdn101 101     10
ip2     fqdn102 102     11
ip3     fqdn103 103     12
ip4     fqdn104 104     13
ip1     fqdn101 105     14
ip1     fqdn102 106     15
ip1     fqdn103 107     16
ip1     fqdn104 108     17
ip2     fqdn103 109     16
ip2     fqdn104 110     17

那应该输出

ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip2 fqdn103 109 16

ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip1 fqdn104 108 17

对 awk 以外的选项开放 - 虽然那将是我的默认起点 - 只要它们在公司 Linux 服务器构建上可用,我就得到...

既然你说“绝对速度比内存消耗更重要”,下面是如何使用最少的内存快速完成你想做的事情:

$ cat tst.sh
#!/usr/bin/env bash

tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"; exit' 0

sort -k1,1 -k2,2 "${@:--}" |
awk '
    { key =  " "  }
    key != prev {
        if ( NR>1 ) {
            print prev, tot3, tot4
        }
        tot3 = tot4 = 0
        prev = key
    }
    {
        tot3 += 
        tot4 += 
    }
    END {
        print key, tot3, tot4
    }
' > "$tmp"

sort -nrk3,3 "$tmp" | head -3
printf '\n'
sort -nrk4,4 "$tmp" | head -3

$ ./tst.sh file
ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip2 fqdn103 109 16

ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip1 fqdn104 108 17

上面唯一必须一次处理整个输入的工具是 sort,它设计用于使用请求分页等来处理大文件,因此不需要能够将整个输入存储在内存中以便能够工作。上面使用的临时文件将比您的原始输入文件小得多,因为它只存储每个密钥对的总数。

如果您不想使用临时文件,那么您可以这样做,这将花费更长的时间(可能是 1.5 倍?)到 运行:

$ cat tst.sh
#!/usr/bin/env bash

do1tot() {
    local totPos=""
    shift

    sort -k1,1 -k2,2 "${@:--}" |
    awk '
        { key =  " "  }
        key != prev {
            if ( NR>1 ) {
                print prev, tot3, tot4
            }
            tot3 = tot4 = 0
            prev = key
        }
        {
            tot3 += 
            tot4 += 
        }
        END {
            print key, tot3, tot4
        }
    ' |
    sort -nrk"$totPos,$totPos" |
    head -3
}

do1tot 3 "${@:--}"
printf "\n"
do1tot 4 "${@:--}"

$ ./tst.sh file
ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip2 fqdn103 109 16

ip1 fqdn101 206 24
ip2 fqdn104 110 17
ip1 fqdn104 108 17