为什么这个 Powershell ForEach 循环每次迭代都会变慢?

问题描述 投票:0回答:1

我的代码按预期工作。 我真的很想知道是否有人知道为什么我在下面描述的事情可能会发生。也就是说,如果有人有任何进一步优化例程的想法,我会本着每天都是上学日的精神感激地接受他们!

脚本正在查询我们所有的域控制器,以获取特定 OU 中所有用户的最新 lastLogon 属性。 (我这样做而不是使用 lastLogonTimeStamp 属性,因为我需要脚本运行时的绝对最新登录。)

在测试期间,为了检查代码是否按照我的预期执行,我添加了一些控制台输出(包含在下面的代码片段中)。

当我这样做时,我注意到在 SECOND

ForEach ( $DC in $AllDCs )
循环的每次迭代中,在嵌套循环写入其第一行控制台输出之前都有一个明显的停顿。暂停的时间随着外层循环的每次迭代而增加,内层循环后续输出的速度也明显下降。在运行过程中,查看十几个 DC 的输出,我估计写入控制台行的速率至少下降了 4 倍。

$AllDCs = Get-ADDomainController -Filter *

$AllRecords = @{}

ForEach ( $DC in $AllDCs ) {

    $UserList = Get-ADUser -filter * -Server $DC -SearchBase $ini.OUDN.NewStartsInternal -SearchScope OneLevel -Properties lastLogon
    $UserList = $UserList | Where { $_.SamAccountName -match $ini.RegEx.PayNo }

    $AllRecords.Add($DC.Hostname,$UserList)

}

$Logons = @{}

ForEach ( $DC in $AllDCs ) {                                   ; this loop is the one I'm talking about
    
    ForEach ( $User in $AllRecords.$($DC.HostName) ) {

        If ( $Logons.ContainsKey($User.SamAccountName) ) {     ;this line amended on advice from mklement0
            
            If ( $Logons.$($User.SamAccountName) -lt $User.lastLogon ) {
            
                $Logons.$($User.SamAccountName) = $User.lastLogon
                Write-Host "Updated $($User.SamAccountName) to $($User.lastLogon)" -ForegroundColor Green

            } Else {

                Write-Host "Ignored $($User.SamAccountName)"

            }

        } Else {

            $Logons.Add( $User.SamAccountName , $User.lastLogon )

            Write-Host "Created $($User.SamAccountName)" -ForegroundColor Yellow

        }

    }

}

我在这里没有任何时间限制,因为我们只讨论了几百个用户和十几个域控制器。无论如何,我已经大大减少了运行时间。以前它是遍历用户并为每个用户一个一个地查询每个 DC,这毫不奇怪,花费的时间要长得多。

更新:

我从下面的第一条评论中实施了 mklement0 的建议,如果有的话,脚本实际上运行得比以前慢了。外循环迭代之间的延迟更长,内循环的输出似乎受到随机延迟的影响。平均而言,我会说内部循环每秒进行大约 2 到 3 次迭代,在我看来,这对于循环遍历已保存在本地内存中的数据来说非常慢。我知道 PowerShell 以速度慢而著称,但这似乎很特别。

脚本通常在 VM 上运行,所以我在自己的计算机上对其进行了测试,速度慢了很多,所以这不是 VM 的资源问题。

更新 2:

我删除了所有

Write-Host
命令并简单地为外循环的每次迭代计时。

首先,删除所有控制台写入会显着提高性能,这是我所期望的,尽管我没有意识到有多少。它很容易将运行时间缩短到原来的五分之一。

就循环时间而言,奇怪的行为仍然存在。在十二次迭代中,前七次在 1 秒内完成,完成最后五次大约需要 35 秒。这种行为每次都或多或少地重复。与可能减慢哈希表查找速度的前七个相比,最后五个 DC 的主机名没有什么不同。

我对现在的运行时间非常满意,但仍然对这种奇怪的行为感到非常困惑。

powershell loops foreach active-directory
1个回答
1
投票

所以,这是我对你的代码的看法,我没有改变很多东西,但我觉得这应该更快一点,但我可能错了。

注意,我使用

LDAPFilter = '(LastLogon=*)'
而不是
Filter = '*'
因为,如果它是一个未在域中复制的属性,它将在查询每个域控制器时节省时间。

$AllDCs = Get-ADDomainController -Filter *
$logons = @{}

$params = @{
    LDAPFilter  = '(LastLogon=*)'
    Server      = ''
    SearchBase  = $ini.OUDN.NewStartsInternal
    SearchScope = 'OneLevel'
    Properties  = 'lastLogon'
}

foreach($DC in $AllDCs) {
    $params.Server = $DC
    $UserList = Get-ADUser @params
    
    foreach($user in $UserList) {
        if($user.samAccountName -notmatch $ini.RegEx.PayNo) {
            continue
        }

        if($logons[$user.samAccountName].LastLogon -lt $user.LastLogon) {
            # On first loop iteration should be always entering
            # this condition because
            # $null -lt [datetime]::MaxValue => True AND
            # $null -lt [datetime]::MinValue => True
            # Assuming $user.LastLogon is always a populated attribute
            # which also explains my LDAPFilter = '(LastLogon=*)' from before

            $logons[$user.samAccountName] = $user
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.