You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化大体积DNS日志处理PowerShell脚本的运行速度

DNS日志解析脚本性能优化

我有一个PowerShell脚本,功能正常、输出完全符合需求,但处理500MB左右、包含600万行的DNS日志时耗时过长。虽明白大数据处理需要时间,但仍希望找到优化方法。以下是精简后的脚本和DNS日志示例:

原脚本

$DnsFilePath = "C:\dns.log"

Param([string]$DnsFilePath)
If (Test-Path $DnsFilePath) 
    { 
        $FileInfo = Get-ChildItem -Path $DnsFilePath
        $Ans = Read-Host "Do you want to continue(y/n)?"
        
        If ($Ans -eq 'y')
            {
                If (!($SkipLines)) { Write-Host "Processing..."; }
                $i = 0; ## 记录处理的条目数
                $Timer= [Diagnostics.Stopwatch]::StartNew() ## 启动计时器
                $ArrayOfStrings = [System.Collections.ArrayList]@()

                Switch -regex ([System.IO.File]::ReadLines($FileInfo.fullname)) {
                ' UDP Rcv ' {
                    $Datetime = [regex]::matches($switch.current,'\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)').Value
                    $IP = [regex]::matches($switch.current,'\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b').Value
                    $FQDN = [regex]::matches($switch.current,"\)[A-z0-9-_]*\(").Value  -replace "\)|\(","" -join "."
                    [void]$ArrayOfStrings.Add("$Datetime,$IP,$FQDN")
                    $i++;
                            }
                }
                        $OutFilePath = "$($FileInfo.DirectoryName)\$($FileInfo.BaseName)_Parsed.txt"
                        [System.IO.File]::WriteAllLines($OutFilePath, $ArrayOfStrings)
                        $Timer.stop()
                        Write-host "Total time elapsed: $($Timer.Elapsed.ToString('hh\:mm\:ss\.ff'))"
                        Write-Host "Number of Record Processed: $i"
                        Write-Host "Parsed File created successfully at $OutFilePath"                       
            }
        else
            { Write-Host "Script exits." }
    }
Else
    {
    Write-Host -fore Red "File does not exist in the following location: $DnsFilePath. Script exits."
    }

DNS日志示例

DNS Server log file creation at 7/10/2023 10:55:42 AM
Log file wrap at 7/10/2023 10:55:42 AM

Message logging key (for packets - other items use a subset of these fields):
    Field #  Information         Values
    -------  -----------         ------
       1     Date
       2     Time
       3     Thread ID
       4     Context
       5     Internal packet identifier
       6     UDP/TCP indicator
       7     Send/Receive indicator
       8     Remote IP
       9     Xid (hex)
      10     Query/Response      R = Response
                                 blank = Query
      11     Opcode              Q = Standard Query
                                 N = Notify
                                 U = Update
                                 ? = Unknown
      12     [ Flags (hex)
      13     Flags (char codes)  A = Authoritative Answer
                                 T = Truncated Response
                                 D = Recursion Desired
                                 R = Recursion Available
      14     ResponseCode ]
      15     Question Type
      16     Question Name

7/10/2023 10:55:42 AM 1B7C PACKET  000001D9D88C68D0 UDP Rcv 8.8.8.8         5fb1 R Q [8381   DR NXDOMAIN] A      (3)www(12)autodiscover(5)st1ad(4)emea(15)microsoftonline(3)com(0)

7/10/2023 10:55:42 AM 1B7C PACKET  000001D9D775F890 UDP Snd 10.x.x.x     92cb R Q [8381   DR NXDOMAIN] A      (3)www(12)autodiscover(5)st1ad(4)emea(15)microsoftonline(3)com(0)

7/10/2023 10:55:42 AM 1B7C PACKET  000001D9E4E338D0 UDP Rcv 10.x.x.x  a9bd   Q [0001   D   NOERROR] A      (18)addinsinstallation(5)store(6)office(3)com(0)

7/10/2023 10:55:42 AM 1B7C PACKET  000001D9D775F890 UDP Snd 8.8.8.8         afda   Q [0001   D   NOERROR] A      (23)prod-addinsinstallation(15)omexexternallfb(6)office(3)net(6)akadns(3)net(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E182BB80 UDP Rcv 10.x.x.x  d229   Q [0001   D   NOERROR] SOA    (15)pc_host01(7)contoso(5)local(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E182BB80 UDP Snd 10.x.x.x  d229 R Q [8085 A DR  NOERROR] SOA    (15)pc_host02(7)contoso(5)local(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E2A2D670 UDP Rcv 8.8.8.8         c95c R Q [8081   DR  NOERROR] A      (9)dtr-a-ncu(2)na(8)azurerms(3)com(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E1998D80 UDP Snd 10.x.x.x     2047 R Q [8081   DR  NOERROR] A      (6)portal(8)azurerms(3)com(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E2D07D00 UDP Rcv 10.x.x.x   788e   Q [0001   D   NOERROR] A      (2)tr(11)c1182306347(12)ip4-58f0802d(4)wgcs(7)skyhigh(5)cloud(0)

7/10/2023 10:55:42 AM 1B78 PACKET  000001D9E1998D80 UDP Snd 8.8.8.8         1c22   Q [0001   D   NOERROR] A      (2)tr(11)c1182306347(12)ip4-58f0802d(4)wgcs(7)skyhigh(5)cloud(0)

优化方案

针对大文件场景,主要从内存占用和正则匹配效率两个核心方向优化:

1. 预编译正则表达式

避免每次匹配都重新生成正则对象,提前编译并复用,减少重复开销:

$regexDatetime = [regex]::new('\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)', [System.Text.RegularExpressions.RegexOptions]::Compiled)
$regexIP = [regex]::new('\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b', [System.Text.RegularExpressions.RegexOptions]::Compiled)
$regexFQDNParts = [regex]::new('\((\d+)\)([A-z0-9-_]+)', [System.Text.RegularExpressions.RegexOptions]::Compiled)

2. 直接逐行写入输出文件

放弃用ArrayList缓存所有结果,改用文件流逐行写入,大幅降低内存占用,避免百万级数据的内存扩容开销:

$OutFilePath = "$($FileInfo.DirectoryName)\$($FileInfo.BaseName)_Parsed.txt"
$writer = [System.IO.StreamWriter]::new($OutFilePath)

3. 简化FQDN解析逻辑

用单个正则捕获所有FQDN片段,直接拼接成完整域名,避免多次替换和合并操作:

$fqdnParts = $regexFQDNParts.Matches($line)
$FQDN = ($fqdnParts | ForEach-Object { $_.Groups[2].Value }) -join '.'

优化后的完整脚本

Param([string]$DnsFilePath = "C:\dns.log")

If (Test-Path $DnsFilePath) 
{ 
    $FileInfo = Get-ChildItem -Path $DnsFilePath
    $Ans = Read-Host "Do you want to continue(y/n)?"
    
    If ($Ans -eq 'y')
    {
        If (!($SkipLines)) { Write-Host "Processing..."; }
        $recordCount = 0
        $timer = [Diagnostics.Stopwatch]::StartNew()

        # 预编译所有需要的正则表达式
        $regexFilter = [regex]::new(' UDP Rcv ', [System.Text.RegularExpressions.RegexOptions]::Compiled)
        $regexDatetime = [regex]::new('\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)', [System.Text.RegularExpressions.RegexOptions]::Compiled)
        $regexIP = [regex]::new('\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b', [System.Text.RegularExpressions.RegexOptions]::Compiled)
        $regexFQDNParts = [regex]::new('\((\d+)\)([A-z0-9-_]+)', [System.Text.RegularExpressions.RegexOptions]::Compiled)

        $OutFilePath = "$($FileInfo.DirectoryName)\$($FileInfo.BaseName)_Parsed.txt"
        # 初始化文件写入流
        $writer = [System.IO.StreamWriter]::new($OutFilePath)

        try {
            foreach ($line in [System.IO.File]::ReadLines($FileInfo.FullName)) {
                if ($regexFilter.IsMatch($line)) {
                    $datetime = $regexDatetime.Match($line).Value
                    $ip = $regexIP.Match($line).Value
                    $fqdnParts = $regexFQDNParts.Matches($line)
                    $fqdn = ($fqdnParts | ForEach-Object { $_.Groups[2].Value }) -join '.'
                    
                    $writer.WriteLine("$datetime,$ip,$fqdn")
                    $recordCount++
                }
            }
        }
        finally {
            $writer.Dispose() # 确保文件流被正确关闭
        }

        $timer.Stop()
        Write-Host "Total time elapsed: $($timer.Elapsed.ToString('hh\:mm\:ss\.ff'))"
        Write-Host "Number of Record Processed: $recordCount"
        Write-Host "Parsed File created successfully at $OutFilePath"                       
    }
    else
    { 
        Write-Host "Script exits." 
    }
}
Else
{
    Write-Host -ForegroundColor Red "File does not exist in the following location: $DnsFilePath. Script exits."
}

内容的提问来源于stack exchange,提问作者Arbelac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:54:55