最近心相不错,所以打算翻译一些英文文档做福利,原文在此,翻译有不足的地方还请各位兄弟指点.
讨论什么是DBA最重要的工作的时候,你最常听到就是一条就是DBA只要做好备份和恢复.事实如此,如果你不做备份,或者无法保证你的备份能够有效恢复,你和你的公司就会处于数据丢失危险下.
T-SQL 命令BACKUP DATABASE已经使用了相当长的一段时间(在这之前用的是DUMP DATABASE 命令,老人们都记得). 它仍然不失为备份数据库好方式.我们现在有一个新的方法来自动的做这些日常的备份处理任务.在本文中我会通过实例来介绍如何通过PowerShell脚本来备份.并告诉大家在SQL Server 2012 使用PowerShell Cmdlet的便利性.
在SQL Server 2012 中,微软增加了4个新的备份和还原的cmdlet
为什么要通过外部程序来做脚本备份?
当备份工作越来越复杂,越来越多文件系统要被牵涉进去的时候.这些工作可能需要建立并按放备份文件至某个目录,删掉不需要的旧备份,异地拷贝,检查备份完整性,写入日志等等. 你也可能想一步操作就把在不同服务器上的多个数据库进行备份或者拷贝数据库到几个服务器或者虚拟机上做测试.这个时候备份和还原就需要考虑使用脚本,而PowerShell擅长此道.
在2009年我发布了白皮书backup.ps1 ,使用SMO(Server Management Objects)库来备份你的数据库.SMO支持SQL Server versions 2000 ~ 2012, 微软在SQL Server 2012并没有给SMO增加多少新的特性.所以没多少新东西可学.
在白皮书中我讲述了SQL Server snapins , 还有SQL Server 2008 和 SQL Server 2008 R2 里面的迷你Shell (SQLPS.exe) ,PowerShell 团队为 SQL Server 2008 扩展的,现在技术已经改变 , 采用”模块”来扩展Powershell 因此, SQL Server 2012 安装的时候Powershell 最低版本是2.0 , 然后 SQLPS.exe 程序已经被替换掉了(叫SQLPS = =).
微软在SQLPS模块中提供了一些新的cmdlets, 多数专门用来管理可用性组和高可用性还有灾难恢复. 他们新增了备份和还原操作的 cmdlets , Backup-SqlDatabase, Restore-SqlDatabase, Backup-ASDatabase 还有 Restore-ASDatabase.
启动 PowerShell 并且想要使用SQLPS模块, 你需要导入相应的cmdlet. 开启PowerShell 2.0,当导入模块的时候微软会通过一个列表来进行动词的检查.Backup 和 Restore并不在这个列表里面,所以你执行模块导入的时候会报错. 你可以使用 -DisableNameChecking 参数来避免这个错误
(译者注:我执行的时候报错. 提示SQLPS不被信任,所以模块导入出错了, 此时要执行Set-ExecutionPolicy RemoteSigned 来开启)
当你导入模块以后,可以先简单的建立个备份数据库的脚本,如下:
{$dt = Get-Date -Format yyyyMMddHHmmss $dbname = 'AdventureWorks' Backup-SqlDatabase -ServerInstance TESTSQL -Database $dbname -BackupFile "E:\Backup\$($dbname)_db_$($dt).bak"
有很多的方法来调用这个指令,比如说你可以使用Get-ChildItem来获取你定位到的数据库实例的目录数据库名,然后通过管道发送给Backup-SqlDatabase命令,进行迭代的进行数据库备份 . 因为你已经连到你的实例这里所以就不需要在加 -ServerInstance 参数了.
简单打下面两句就了…
Set-Location SQLSERVER:\SQL\TESTSQL
\DEFAULT\Databases get-childitem|Backup-SqlDatabase
备份会放在默认的备份目录里面, 备份文件名也是默认的数据库名字. 如果你想指定备份文件的名字,或者其他一些阐述,可以用下面的代码
Set-Location SQLSERVER:\SQL\TESTSQL\DEFAULT\Databases foreach ($db in (Get-ChildItem)) { $dbname = $db.Name $dt = Get-Date -Format yyyyMMddHHmmss Backup-SqlDatabase -Database $dbname -BackupFile "$($dbname)_db_$($dt).bak" }
下面段会备份所有用户数据库被分到默认备份目录,并且以数据库的名字和当前日期时间作为备份文件的名字. (如果你使用 Get-ChildItem 加上-Force 参数,那就会包含系统数据库,当然 tempdb被Where过滤掉了.)
get-childitem -force|where name -ne 'TempDB'| Backup-SqlDatabase
你也可以建立一个包含 SMO Server 对象的脚本 , 用 -InputObject 代替-ServerInstance . 虽然我们示例是备份一个数据库,但是这个在处理多服务器上的数据库的时候会很有用.
$dt = Get-Date -Format yyyyMMddHHmmss $dbname = 'AdventureWorks' $svr = new-object ('Microsoft.SqlServer.Management.Smo.Server') 'TESTSQL' Backup-SqlDatabase -InputObject $svr -Database $dbname -BackupFile "E:\Backup\$($dbname)_db_$($dt).bak"
另外你也可以使用一个包含SMO数据库的变量,然后使用 -DatabaseObject 参数.
$dt = Get-Date -Format yyyyMMddHHmmss Set-Location SQLSERVER:\SQL\TESTSQL\DEFAULT\Databases\AdventureWorks $db = Get-Item . $dbname = $db.Name Backup-SqlDatabase -DatabaseObject $db -BackupFile "E:\Backup\$($dbname)_db_$($dt).bak"
以上示例为了便于理解,均使用了最少的参数,实际操作的话,你应该看一下其他参数,使得备份方案符合你的需求.
我通常会加参数 -BackupAction. 选项可以是Database,Files和Log,默认是Database, 如果你想做差异备份,那么指定 BackupAction 为Database ,然后加上-Incremental参数.如果你想备份单个文件或者文件组,你可以使用 Files 选项.而 Log 选项允许你做事务日志备份.为了节省篇幅,因此去掉了这些参数. 所以例子直接备份了整个数据库.
举个例子,我总是会使用-CompressionOption 参数. 当然还有-ConnectionTimeout ,并且设置为0 ,因为你不想备份的时候因为超时而被中断.
我们用get-help 获得 Backup-SqlDatabase 的参数列表 :
Backup-SqlDatabase
[-Database] <string>
[-BackupFile] <string[]>]
[-ServerInstance <string[]> ]
[-BackupAction <BackupActionType>]
[-BackupDevice <BackupDeviceItem[]>]
[-BackupSetDescription <string>]
[-BackupSetName <string>]
[-BlockSize <int>]
[-BufferCount <int>]
[-Checksum]
[-CompressionOption <BackupCompressionOptions>]
[-ConnectionTimeout <int>]
[-ContinueAfterError]
[-CopyOnly]
[-Credential <PSCredential>]
[-DatabaseFile <string[]>]
[-DatabaseFileGroup <string[]>]
[-ExpirationDate <DateTime>]
[-FormatMedia]
[-Incremental]
[-Initialize]
[-LogTruncationType <BackupTruncateLogType>]
[-MaxTransferSize <int>]
[-MediaDescription <string>]
[-MediaName <string>]
[-MirrorDevices <BackupDeviceList[]>]
[-NoRecovery]
[-NoRewind]
[-Passthru]
[-Restart]
[-RetainDays <int>]
[-Script]
[-SkipTapeHeader]
[-UndoFileName <string>]
[-UnloadTapeAfter]
[-Confirm]
[-WhatIf]
[<CommonParameters>]
最明显的就是在命令集中增加了一些SMO没有的功能.比如通过Transact-SQL可以设置备份的块大小和缓冲区数而SMO没有.下图是等价功能的SMO的备份对象的属性和方法:
(上图是通过Visual Studio 的 Object Browser 载入SMOExtended DLL的截图)
总归有各种各样的原因需要还原数据库, 所以…还原的选项比备份的还要多.
下面是恢复full backup一个简单示例, 选项设置直接覆盖现有数据库.
Restore-SqlDatabase -ServerInstance TESTSQL -Database AdventureWorks ` -BackupFile "E:\Backup\AdventureWorks_db_20130420153024.bak" -ReplaceDatabase
我还原数据库最频繁的理由之一就是用户不小心删掉了数据,随后又被事务更新,导致数据丢失.碰到这种情况我就要用另外一个名字还原该数据库,然后把需要的数据复制过去.
下面是我们使用SMO还原的代码
# Connect to the specified instance $srv = new-object ('Microsoft.SqlServer.Management.Smo.Server') 'TESTSQL' # Get the default file and log locations # (If DefaultFile and DefaultLog are empty, use the MasterDBPath and MasterDBLogPath values) $fileloc = $srv.Settings.DefaultFile $logloc = $srv.Settings.DefaultLog if ($fileloc.Length -eq 0) { $fileloc = $srv.Information.MasterDBPath } if ($logloc.Length -eq 0) { $logloc = $srv.Information.MasterDBLogPath } # Identify the backup file to use, and the name of the database copy to create $bckfile = 'E:\Backup\AdventureWorks_db_20101016135438.bak' $dbname = 'AdventureWorks_20101016' # Build the physical file names for the database copy $dbfile = $fileloc + '\'+ $dbname + '_Data.mdf' $logfile = $logloc + '\'+ $dbname + '_Log.ldf' # Use the backup file name to create the backup device $bdi = new-object ('Microsoft.SqlServer.Management.Smo.BackupDeviceItem') ($bckfile, 'File') # Create the new restore object, set the database name and add the backup device $rs = new-object('Microsoft.SqlServer.Management.Smo.Restore') $rs.Database = $dbname $rs.Devices.Add($bdi) # Get the file list info from the backup file $fl = $rs.ReadFileList($srv) foreach ($fil in $fl) { $rsfile = new-object('Microsoft.SqlServer.Management.Smo.RelocateFile') $rsfile.LogicalFileName = $fil.LogicalName if ($fil.Type -eq 'D'){ $rsfile.PhysicalFileName = $dbfile } else { $rsfile.PhysicalFileName = $logfile } $rs.RelocateFiles.Add($rsfile) } # Restore the database $rs.SqlRestore($srv)
还原一个已存在的数据库的问题就是物理名称冲突,要解决这个问题SMO 可以使用RelocateFile 对象. 这和Transact-SQL 使用 WITH MOVE 条件一样. 有趣的是Restore-SqlDatabase 命令也使用SMO 对象处理这个事情.
使用纯SMO 和 Restore-SqlDatabase 命令的唯一区就是何存储和传递RelocateFile 对象. 建立一个空的集合,然后读取备份文件列表,并且把每个RelocateFile 的每个对象都添加到集合中. 随后调用 Restore-SqlDatabase命令. 下图中最后最后一部分用的是新的cmdlet,其他上面的例子都一样.
# Get the file list info from the backup file $fl = $rs.ReadFileList($srv) $rfl = @() foreach ($fil in $fl) { $rsfile = new-object('Microsoft.SqlServer.Management.Smo.RelocateFile') $rsfile.LogicalFileName = $fil.LogicalName if ($fil.Type -eq 'D') { $rsfile.PhysicalFileName = $dbfile } else { $rsfile.PhysicalFileName = $logfile } $rfl += $rsfile } # Restore the database Restore-SqlDatabase -ServerInstance TESTSQL -Database $dbname ` -BackupFile "E:\Backup\AdventureWorks_db_20101016135438.bak" ` -RelocateFile $rfl
在本例中我贴出了纯SMO的例子,应该已经很清楚了.
最后一个例子是通过一个时间点来还原数据库. 有时候你知道问题发生的时间,你可以恢复到那个时间点.
在这个情境中我们所有的被封文件都放在本地服务器的 E:\Backup 目录. 我们有很多完全备份,差异备份,还有一些事务日志备份文件用来做某一个时间点的还原.文件名格式为 DatabaseName_type_datetime.ext, 其中type是db, diff 和 tran ,ext是 bak 或 trn.
$dbname = 'AdventureWorks' $restorept = '2013-04-20 15:30:00' Set-Location 'E:\Backup' $fullfile = Get-ChildItem -Filter "$($dbname)_db_*" | Where-Object {$_.LastWriteTime -lt $restorept} | Sort-Object LastWriteTime Desc | Select-Object -First 1 $difffile = Get-ChildItem -Filter "$($dbname)_diff_*" | Where-Object {$_.LastWriteTime -lt $restorept} | Sort-Object LastWriteTime Desc | Select-Object -First 1 $tranfile = Get-ChildItem -Filter "$($dbname)_tran_*" | Where-Object {$_.LastWriteTime -gt $difffile.LastWriteTime} | Sort-Object LastWriteTime Asc
$fullfile 变量包含指定时间点前的完整备份文件信息,$difffile变量包含指定时间点前的差异备份文件信息 , $tranfile 包含差异备份后至指定时间点前的所有的事务日志文件信息
首先我们需要还原完整备份, 并且指定 no recovery选项.
Restore-SqlDatabase -ServerInstance TESTSQL -Database $dbname ` -BackupFile $fullfile.FullName -ReplaceDatabase ` -NoRecovery
然后我们还原最后一个差异备份,同样的,指定no recovery选项.
Restore-SqlDatabase -ServerInstance TESTSQL -Database $dbname ` -BackupFile $difffile.FullName -ReplaceDatabase ` -NoRecovery
最后我们通过循环来读取$tranfile变量中的文件来还原事务日志, 如果LastWriteTime 属性比 $restorept 变量小,那么恢复的时候指定no recovery,然后处理下一个,否则,则根据-ToPointInTime 参数指定的时间点来还原. 不加-NoRecovery 参数,至此整个还原过程结束.
$recovery = 0 foreach ($trnfile in $tranfile) { if ($trnfile.LastWriteTime -lt $restorept) { Restore-SqlDatabase -ServerInstance TESTSQL -Database $dbname ` -BackupFile $trnfile.FullName -ReplaceDatabase ` -NoRecovery } else { if ($recovery -eq 0) { Restore-SqlDatabase -ServerInstance TESTSQL -Database $dbname ` -BackupFile $trnfile.FullName -ReplaceDatabase ` -ToPointInTime $restorept $recovery = 1 } } }
还有很多特性,结合通过操作系统的一些功能来做备份和还原操作,比用Transact-SQL要方便许多
下图是SMO Restore 对象的属性和方法:
相应的下面是Restore-SqlDatabase的参数
Restore-SqlDatabase
[-Database] <string>
[[-BackupFile] <string[]>]
-ServerInstance <string[]>
[-BackupDevice <BackupDeviceItem[]>]
[-BlockSize <int>]
[-BufferCount <int>]
[-Checksum]
[-ClearSuspectPageTable]
[-ConnectionTimeout <int>]
[-ContinueAfterError]
[-Credential <PSCredential>]
[-DatabaseFile <string[]>]
[-DatabaseFileGroup <string[]>]
[-FileNumber <int>]
[-KeepReplication]
[-MaxTransferSize <int>]
[-MediaName <string>]
[-NoRecovery]
[-NoRewind]
[-Offset <Int64[]>]
[-Partial]
[-Passthru]
[-RelocateFile <RelocateFile[]>]
[-ReplaceDatabase]
[-Restart]
[-RestoreAction <RestoreActionType>]
[-RestrictedUser]
[-Script]
[-StandbyFile <string>]
[-StopAtMarkAfterDate <string>]
[-StopAtMarkName <string>]
[-StopBeforeMarkAfterDate <string>]
[-StopBeforeMarkName <string>]
[-ToPointInTime <string>]
[-UnloadTapeAfter]
[-Confirm]
[-WhatIf]
[<CommonParameters>]
Analysis Services 的数据库也是需要备份的,虽然没多少选项可用. 首先你需要载入Analysis Services 命令集的模块 .
Import-Module SQLASCMDLETS
我们先看下 Backup-ASDatabase的所有参数.
Backup-ASDatabase
[-BackupFile] <string>
[-Name] <string>
[-AllowOverwrite <SwitchParameter>]
[-BackupRemotePartitions <SwitchParameter>]
[-ApplyCompression <SwitchParameter>]
[-FilePassword <SecureString>]
[-Locations <Microsoft.AnalysisServices.BackupLocation[]>]
[-Server <string>]
[-Credentials <PSCredential>]
[<CommonParameters>]
注意:这里没有差异备份或者日志备份的选项.下面是备份AWDB database一个简单例子.
Backup-ASDatabase "E:\Backup\AWDB.abf" AWDB
如果需要覆盖已存在的数据库备份,可以加上 -AllowOverwrite 参数,需要压缩备份则加上-ApplyCompression参数, 要加密的话可以使用 -FilePassword 参数.
同样的还原Analysis Services 数据库也没多少可用选项.
Restore-ASDatabase
[-RestoreFile] <string>
[-Name] <System.String>
[-AllowOverwrite <SwitchParameter>]
[-Locations <Microsoft.AnalysisServices.RestoreLocation[]>]
[-Security <Microsoft.AnalysisServices.RestoreSecurity>]
[-Password <System.SecureString>]
[-StorageLocation <System.String>]
[-Server <string>]
[-Credentials <PSCredential>] [<CommonParameters>]
下面是一个还原例子
Restore-ASDatabase "E:\Backup\AWDB.abf" AWDB -Security:CopyAll
还原的时候同事恢复roles和members
组织数据的备份还原是至关重要的.使用Powershell和SMO编写脚本来管理SQL SERVER的备份还原是最好的,尤其是DBA需要完成更复杂,的重复性的任务的时候. 比如数据库需要高可用性, 我们可以创建脚本来重建他们,进行高可用性测试排练.总之 在 SQL Server 2012中使用他们最理想的,而且很方便.