За получение списка БД на сервере отвечает функция GetDbList. Она принимает в качестве опционального параметра имя экземпляра SQL-сервера. Если имя не указано, подключение идет к экземпляру по умолчанию. Для каждой базы данных получаем ее имя, состояние и принадлежность к системным базам.
Однако во время отладки выявилась одна неприятная особенность — получение списка таким способом занимало более двух с половиной минут для 120 баз данных. При этом аналогичный SQL-запрос отрабатывал задачу практически мгновенно. Отладка в профайлере SQL показала, что при выполнении кода, показанного ниже, SMO генерируют множество запросов к каждой БД вместо одного простого, что и приводит к потере производительности.
$SqlServer = New-Object ("Microsoft. SqlServer. Management.
SMO. Server") $Instance $DbList = $SqlServer. Databases | Select-Object Name, Status, IsSystemObject
Оказалось, что это проблема известная и существует способ ее решения [10]. Нужно заранее указать SMO, к каким полям БД мы будем обращаться, что позволит им генерировать оптимальные SQL-запросы. Сделать это можно с помощью метода SetDefaultlnitFields.
SMO всегда запрашивают имя и схему базы данных, поэтому нам требуется указать только два поля, которые нужно дополнительно получить при запросе к SQL: состояние БД Status и ее принадлежность к системным объектам IsSystemObject. Однако метод SetDefaultlnitFields принимает список параметров только в том случае, если это коллекция строк (StringCollection из. Net), а строки в PowerShell таковыми не являются. Поэтому необходимо создать объект с коллекцией строк, добавить в него наши поля, передать методу и уже после этого пытаться получить список баз.
[array]$Preload ="Status", "IsSystemObject"
$StrCol = New-Object System. Collections. Specialized.
StringCollection
$StrCol. AddRange ($Preload)
$SqlServer. SetDefaultlnitFields ([Microsoft. SqlServer.
Management. SMO. Database], $StrCol)
После такой оптимизации получение списка баз данных стало занимать порядка 13 секунд.
Поскольку упомянутый выше способ подключения с помощью модуля SQLPS также использует внутри себя SMO, то проблема аналогична. И решается она подобным же способом.
$SqlServer= Get-Item SqlServer:SQLServerNameDefault
[array]$Preload ="Status", "IsSystemObject"
$StrCol = New-Object System. Collections. Specialized.
StringCollection
$StrCol. AddRange ($Preload)
$SqlServer. SetDefaultlnitFields ([Microsoft. SqlServer.
Management. SMO. Database], $StrCol)
$SqlServer. Databases | Select Name, Status, IsSystemObject
Запускутилиты SQL Server Compressed Backup
Привычный способ запуска внешних утилит с использованием символа амперсанда «&», который указывает Powershell не обрабатывать идущие за ним символы как имя командлета или строку, а выполнить ее как внешнюю команду, не работает в случае с SQL Server Compressed Backup. Дело в том, что утилита активно применяет кавычки внутри параметров, а при использовании «&» PowerShell передает все аргументы как одну строку, оборачивая их в кавычки. В большинстве случаев это работает, однако в данном варианте парсер командной строки в программе не справляется с подобной конструкцией. Поэтому следует воспользоваться командлетом Start-Process, который является надстройкой над. NET методом Process. Start. Он позволяет запускать процессы напрямую, минуя обработчик PowerShell.
$BackupJob = Start-Process — FilePath $script: BackupExe
-ArgumentList $LaunchString — NoNewWindow — Wait — PassThru
При этом сохраняется возможность получать код возврата утилиты для проверки успешности резервного копирования.
if ($BackupJob. ExitCode) {
Write-Log "Произошла ошибка при бэкапе $DbName"
} else
{
Write-Log "Бэкап базы $DbName занял $BackupTime
}