SharePoint 2019中PowerShell脚本运行速度异常问题排查
你的脚本运行后期变慢,主要是SharePoint对象未正确释放导致内存泄漏、重复AD查询无缓存、频繁文件追加写入这三个核心问题共同作用的结果,和算法逻辑错误关系不大,但资源管理和操作效率有明显优化空间:
一、核心原因分析
- SharePoint对象内存泄漏:SharePoint的
SPSite、SPWeb属于非托管资源,必须手动调用Dispose()释放。你的脚本仅在循环结束后调用了$site.Dispose(),但循环内每个$site以及$site.RootWeb都没释放,随着处理的站点增多,内存占用持续上升,GC(垃圾回收)压力增大,导致后期操作卡顿。 - AD查询无缓存:每个符合
*ozon.ru的用户都会触发两次AD查询(查用户、查经理),没有缓存已查询过的用户信息,后期重复查询累积,加上AD服务器的响应延迟,拖慢整体速度。 - 频繁文件追加写入:每个数据库处理完就执行一次
Export-Csv -Append和Export-Excel -Append,随着CSV/Excel文件体积增大,每次打开、写入、关闭文件的耗时会越来越长。
二、针对性优化方案
1. 严格释放SharePoint对象
使用try/finally块确保每个SPSite和SPWeb都被释放,彻底避免内存泄漏:
foreach ($site in $database.Sites) { try { # 处理站点逻辑 $rootWeb = $site.RootWeb try { # 使用$rootWeb的业务逻辑,比如获取站点标题 } finally { if ($rootWeb) { $rootWeb.Dispose() } } } finally { $site.Dispose() } }
2. 缓存AD查询结果
用哈希表缓存已查询的用户和经理信息,避免重复调用Get-AdUser,同时将模糊匹配改为精确匹配提升查询效率:
# 初始化缓存哈希表 $adUserCache = @{} $adManagerCache = @{} # 查询用户时优先读取缓存 if (($ownerEmail -ne '') -and ($ownerEmail -like '*ozon.ru')) { if (-not $adUserCache.ContainsKey($ownerEmail)) { $ozon = Get-AdUser -Server "ozon" -Filter { userPrincipalName -eq $ownerEmail } -Properties employeeType, manager $adUserCache[$ownerEmail] = $ozon } else { $ozon = $adUserCache[$ownerEmail] } if ($ozon.manager -ne $null) { if (-not $adManagerCache.ContainsKey($ozon.manager)) { $managerObj = Get-AdUser -Server "ozon" -Filter { distinguishedName -eq $ozon.manager } $adManagerCache[$ozon.manager] = $managerObj.userPrincipalName } $manager = $adManagerCache[$ozon.manager] } }
3. 减少文件写入次数
将所有站点信息汇总到全局集合,最后一次性写入文件,避免频繁的文件IO操作:
# 全局集合存储所有站点数据 $allSiteInfo = [System.Collections.ArrayList]::new() # 循环内仅向集合添加数据 $allSiteInfo.Add([PSCustomObject]@{ ... }) | Out-Null # 所有数据库处理完成后统一写入 $allSiteInfo | Export-Csv -Path "C:\SysOps\SharePointSites.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ';' $allSiteInfo | Export-Excel -Path "C:\SysOps\SharePointSites.xlsx"
4. 其他细节优化
- 提前赋值重复使用的变量,比如
$ownerEmail = $site.Owner.Email,避免多次调用属性。 - 初始化
$QuotaUsage为0,避免未赋值导致的异常。 - 减少不必要的
Write-Host输出,控制台IO操作也会累积耗时,可改为写入日志文件或仅输出关键进度。
三、优化后的完整脚本示例
Add-PSSnapin "Microsoft.SharePoint.PowerShell" Measure-Command { $databases = Get-SPContentDatabase -WebApplication 'https://...' $emptyDB = 0 $fullDB = 0 $totalSites = 0 $allSiteInfo = [System.Collections.ArrayList]::new() $adUserCache = @{} $adManagerCache = @{} foreach ($database in $databases) { if ($database.Sites.Count -eq 0) { Write-Host 'Database ' $database.Name ' is empty' -ForegroundColor Red $emptyDB++ } else { Write-Host 'Database ' $database.Name ' has ' $database.Sites.Count ' sites' -ForegroundColor Green $fullDB++ foreach ($site in $database.Sites) { try { if ($site.Url.Contains("sitemaster")) { Write-Host $site.Url "was excluded from export" -ForegroundColor Yellow continue } $ownerEmail = $site.Owner.Email $siteUrl = $site.Url $siteOwner = if ($ownerEmail -ne '') { $ownerEmail } else { $site.Owner.DisplayName } $siteUsage = [math]::round($site.Usage.Storage / 1GB, 3) $siteLastTimeUsage = $site.LastContentModifiedDate.ToString("yyyy-MM-dd") $siteDatabases = $database.Name $siteQuota = [math]::round($site.Quota.StorageMaximumLevel / 1GB, 3) $QuotaUsage = 0 if ($siteQuota -ne 0) { $QuotaUsage = [math]::round($siteUsage / $siteQuota * 100, 2) } $webTitle = $null $rootWeb = $site.RootWeb try { $webTitle = $rootWeb.Title } finally { if ($rootWeb) { $rootWeb.Dispose() } } $ozon = $null $UserStatus = '' $FiredDate = '' $manager = '' $ozonEnabled = $null if (($ownerEmail -ne '') -and ($ownerEmail -like '*ozon.ru')) { if (-not $adUserCache.ContainsKey($ownerEmail)) { $ozon = Get-AdUser -Server "ozon" -Filter { userPrincipalName -eq $ownerEmail } -Properties employeeType, manager $adUserCache[$ownerEmail] = $ozon } else { $ozon = $adUserCache[$ownerEmail] } $ozonEnabled = $ozon.Enabled if ($ozon.employeeType -like 'Fired*') { $UserStatus = $ozon.employeeType.split()[0] $FiredDate = [datetime]::parseexact($ozon.employeeType.split()[1], 'dd.MM.yyyy', $null).ToString('yyyy-MM-dd') } else { $UserStatus = $ozon.employeeType } if ($ozon.manager -ne $null) { if (-not $adManagerCache.ContainsKey($ozon.manager)) { $managerObj = Get-AdUser -Server "ozon" -Filter { distinguishedName -eq $ozon.manager } $adManagerCache[$ozon.manager] = $managerObj.userPrincipalName } $manager = $adManagerCache[$ozon.manager] } } $allSiteInfo.Add([PSCustomObject]@{ WebTitle = $webTitle SiteURL = $siteUrl Owner = $siteOwner LastTimeUsage = $siteLastTimeUsage DiskUsage = $siteUsage Quota = $siteQuota QuotaUsage = $QuotaUsage Database = $siteDatabases Ozon = $ozonEnabled UserStatus = $UserStatus FireDate = $FiredDate Manager = $manager }) | Out-Null } finally { $site.Dispose() } } } $totalSites += $database.Sites.Count } # 统一写入文件 $allSiteInfo | Export-Csv -Path "C:\SysOps\SharePointSites.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ';' $allSiteInfo | Export-Excel -Path "C:\SysOps\SharePointSites.xlsx" Write-Host 'Empty databases - ' $emptyDB -ForegroundColor Red Write-Host 'Databases with sites - ' $fullDB -ForegroundColor Green Write-Host '---' Write-Host 'Total sites - ' $totalSites -ForegroundColor Green }
内容的提问来源于stack exchange,提问作者Evgeny Trifonov
相关产品推荐
相关产品推荐

