天天看點

使用Powershell實作資料庫自動化運維

使用Powershell實作資料庫自動化運維

目前市場上針對SQLServer的圖書,好的原創圖書屈指可數,翻譯的圖書較多,但隻限于專門針對SQL Server資料庫本身的開發、管理和商業智能。

而針對PowerShell的圖書,可以說大多為MSDN裡的學習筆記,主要針對Windows作業系統的管理。

微軟在大的布局和技術動向來看,力推雲平台。而平台化的基礎是自動化、高可用。那麼細化到最基礎的技術着眼點,微軟正在大力開發其所有伺服器端産品對 PowerShell的支援。針對SQL Server來說,運用好PowerShell這項技能來将管理任務自動化,才能實作進一步的平台化,它是雲平台的基石。

國外大力推廣的DEVOPS、開發型運維,不僅僅讓資料庫管理者或系統管理者局限于手工來操作繁雜的日常工作,這樣風險極大。學習PowerShell來提升腳本開發能力,讓日常工作化繁為簡,是大勢所趨。

我翻譯了一本微軟MVP的《PowerShell V3 -- SQL Server 2012資料庫自動化運維權威指南》,這本書涉及的知識點非常全面、實用性很強。對SQL Server DBA來說,是提高資料庫管理技能的利器。

下面我通過兩個執行個體來講解下,使用PowerShell如何實作對SQL Server和MongoDB的自動化運維的。

一、 恢複SQL Server資料庫到一個時間點

在本方案中,我們将使用不同備份檔案恢複到一個時間點。

準備

在本方案中,我們将使用AdventureWorks2008R2資料庫。你也可以選擇你的開發環境中的你更喜歡的資料庫。

AdventureWorks2008R2資料庫有一個包含一個單獨資料庫檔案的檔案組。我們将使用來自以下三種不同的備份類型的三個不同備份檔案,來基于時間點恢複資料庫到另一個SQL Server執行個體:

  • 完整備份
  • 差異備份
  • 事務日志備份

我們可以使用PowerShell,像在之前的方案描述的,在AdventureWorks2008R2資料庫上建立這三種類型的備份。如果你對T-SQL相當熟悉,你也可以使用T-SQL備份指令。

為了幫助我們驗證是否基于時間點的恢複的結果是我們期待的,在做任何類型的備份之前,建立一個時間戳辨別的表。相應的,建立一個表,并在備份前插入一個時間戳辨別的記錄到表中。

将這些備份放在C:\Backup\目錄。

使用Powershell實作資料庫自動化運維

你可以使用下面的腳本來建立你的檔案,6464 - Ch06 - 10 - Restore a database to a point in time - Prep.ps1,它包含在本書的可下載下傳檔案中。當腳本執行完整後,你應該在AdventureWorks2008R2資料庫中有時間戳辨別的Student表,以一分鐘的間隔建立,類似于下面的截屏:

(譯者注:可以從https://www.packtpub.com/books/content/support/10233下載下傳該書代碼。)

使用Powershell實作資料庫自動化運維

對于我們的方案,我們将恢複AdventureWorks2008R2資料庫到另一個執行個體,KERRIGAN\SQL01,到2015-07-27 02:51:59。意味着,在基于時間點的恢複完成後,我們将隻有四個時間戳辨別的Student表在KERRIGAN\SQL01在恢複的資料庫上:

  • StudentFull_201507270247
  • StudentDiff_201507270249
  • StudentTxn_201507270250
  • StudentTxn_201507270251

如何做…

為了使用完整、差異和一些事務日志檔案恢複到一個時間點,遵循如下步驟:

1. 通過“Start | Accessories | Windows PowerShell | Windows PowerShell ISE”打開PowerShell控制台。

2. 導入SQLPS子產品:  

#import SQL Server module    
Import-Module SQLPS -DisableNameChecking      

3. 添加如下腳本并運作:  

$instanceName = "KERRIGAN\SQL01"    
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $instanceName    
#backupfilefolder    
$backupfilefolder = "C:\Backup\"    
#look for the last full backupfile    
#you can be more specific and specify filename    
$fullBackupFile =     
Get-ChildItem $backupfilefolder -Filter "*Full*" |    
Sort -Property LastWriteTime -Descending |    
Select -Last 1    
#read the filelist info within the backup file    
#so that we know which other files we need to restore    
$smoRestore = New-Object Microsoft.SqlServer.Management.Smo.Restore    
$smoRestore.Devices.AddDevice($fullBackupFile.FullName, [Microsoft.SqlServer.Management.Smo.DeviceType]::File)    
$filelist = $smoRestore.ReadFileList($server)    
#read headers of the full backup file,     
#because we are restoring to a default instance, we will    
#need to specify we want to move the files    
#to the default data directory of our KERRIGAN\SQL01 instance    
$relocateFileList = @()    
$relocatePath = "C:\Program Files\Microsoft SQL Server\MSSQL11.SQL01\MSSQL\DATA"    
#we are putting this in an array in case we have     
#multiple data and logfiles associated with the database    
foreach($file in $fileList)    
{    
#restore to different instance    
#replace default directory path for both     
$relocateFile = Join-Path $relocatePath (Split-Path $file.PhysicalName -Leaf)    
$relocateFileList += New-Object Microsoft.SqlServer.Management.Smo.RelocateFile($file.LogicalName, $relocateFile)    
}    
#let's timestamp our restored databasename    
#this is strictly for testing our recipe    
$timestamp = Get-Date -Format yyyyMMddHHmmss    
$restoredDBName = "AWRestored_$($timestamp)"    
#====================================================    
#restore the full backup to the new instance name    
#====================================================    
#note we have a NoRecovery option, because we have    
#additional files to restore    
Restore-SqlDatabase `    
-ReplaceDatabase `    
-ServerInstance $instanceName `    
-Database $restoredDBName `    
-BackupFile $fullBackupFile.FullName `    
-RelocateFile $relocateFileList `    
-NoRecovery    
#====================================================    
#restore last differential    
#note the database is still in Restoring State    
#====================================================    
#using PowerShell V2 Where syntax    
$diffBackupFile =     
Get-ChildItem $backupfilefolder -Filter "*Diff*" |    
Where {$_.LastWriteTime -ge $fullBackupFile.LastWriteTime} |    
Sort -Property LastWriteTime -Descending |    
Select -Last 1    
Restore-SqlDatabase `    
-ReplaceDatabase `    
-ServerInstance $instanceName `    
-Database $restoreddbname `    
-BackupFile $diffBackupFile.FullName `    
-NoRecovery    
#====================================================    
#restore all transaction log backups from last    
#differential up to 2015-07-27 02:51:59    
#====================================================    
#identify the last txn log backup file we need to restore    
#we need this so we can specify point in time    
$lastTxnFileName = "AdventureWorks2008R2_Txn_201507270251"    
$lastTxnBackupFile =     
Get-ChildItem $backupfilefolder -Filter "*$lastTxnFileName*"    
#restore all transaction log backups after the     
#last differential, except the last transaction    
#backup that requires the point-in-time restore    
foreach ($txnBackup in Get-ChildItem $backupfilefolder -Filter     
"*Txn*" |     
Where {$_.LastWriteTime -ge $diffBackupFile.LastWriteTime -and     
$_.LastWriteTime -lt $lastTxnBackupFile.LastWriteTime} |    
Sort -Property LastWriteTime)    
{    
Restore-SqlDatabase `    
-ReplaceDatabase `    
-ServerInstance $instanceName `    
-Database $restoreddbname `    
-BackupFile $txnBackup.FullName `    
-NoRecovery     
}     
#restore last txn backup file to point in time    
#restore only up to 2015-07-27 02:51:59    
#this time we are going to restore using with recovery    
Restore-SqlDatabase `    
-ReplaceDatabase `    
-ServerInstance $instanceName `    
-Database $restoreddbname `    
-BackupFile $lastTxnBackupFile.FullName `    
-ToPointInTime "2015-07-27 02:51:59"      

如何實作…

在本方案中,我們使用Restore-SqlDatabase cmdlet,與Backup-SqlDatabase相對的cmdlet在SQL Server 2012中被介紹。

讓我們從高層概覽下如何實施時間點恢複,然後我們可以細化,并解釋包含在本方案中的片段:

1. 收集你的備份檔案。

  • 識别包含你想恢複的時間點的最後事務日志備份檔案。

2. 恢複最後的好的完整備份使用NORECOVERY。

3. 恢複最後的在完整備份後的差異備份使用NORECOVERY。

4. 恢複在差異備份後的事務日志備份:

  • 使用NORECOVERY恢複,直到包含你想恢複的時間點的日志檔案備份。你需要恢複最後的日志檔案備份到一個時間點,也就是說,你需要指定需要恢複的時間。最後,使用WITH RECOVERY恢複資料庫,使得資料庫可通路并以備使用。
  • 或者,你可以使用NORECOVERY恢複所有的事務日志備份檔案,在包含你想恢複到的時間點的日志備份前。接下來,使用WITH RECOVERY恢複最後的日志備份到一個時間點,那就是說,你需要指定恢複到何時。

步驟1 – 收集你的備份檔案

你需要收集你的備份檔案。它們不必位于相同的目錄或驅動裝置,但這樣理想些,這樣可以簡化你的恢複腳本,你将有一個統一的目錄或驅動裝置去參照。你也需要這些檔案的讀許可權限。

在我們的方案,我們簡化了這個步驟。我們收集了完整,差異和事務日志備份檔案,存儲它們到C:\Backup\目錄,易于通路。如果你的備份檔案位于不同的位置,你隻需要适當的調整你腳本的參照目錄。

一旦有了這些備份檔案,假設你遵循着檔案命名規範,你可以過濾你目錄中的所有完整備份。在我們的示例中,我們使用命名規範databasename_type_timestamp.bak。對于這個方案,我們通過在檔案名中指定關鍵字或比對模式來抽取完整備份檔案。我們使用Get-ChildItem過濾最新的完整備份檔案:

#look for the last full backupfile
#you can be more specific and specify filename
$fullBackupFile =
Get-ChildItem $backupfilefolder -Filter "*Full*" |
Sort -Property LastWriteTime -Descending |
Select -Last 1      

一旦你有了完整備份的句柄,你可以讀取存儲在備份檔案中的檔案清單。你可以使用SMO Restore對象的ReadFileList方法。讀取檔案清單可以幫助你通過抽取你需要恢複的資料和日志檔案的檔案名來實作自動化。

#read the filelist info within the backup file
#so that we know which other files we need to restore
$smoRestore = New-Object Microsoft.SqlServer.Management.Smo.Restore
$smoRestore.Devices.AddDevice($fullBackupFile.FullName, [Microsoft.
SqlServer.Management.Smo.DeviceType]::File)
$filelist = $smoRestore.ReadFileList($server)      

當讀取檔案清單時,你可以抽取的一個屬性是存儲的檔案的類型:

使用Powershell實作資料庫自動化運維

不同的類型為:

  • L代表日志檔案
  • D代表資料檔案
  • F代表全文目錄

步驟2 – 使用NORECOVERY恢複最後的好的完整備份

在恢複操作的第一步是恢複最後的已知好的完整備份。這提供了你一個基線,基于此你可以恢複額外的檔案。NORECOVERY選項非常重要,它保持(或不復原)未送出的事務,并允許額外的檔案被恢複。我們将會使用NORECOVERY選項在我們真個恢複過程中。

因為完整備份總是第一個需要恢複的檔案,所有的準備工作需要就緒,此時移動檔案也開始。

對于我們的方案,我們想去恢複資料庫,從源預設執行個體KERRIGAN到另一個執行個體KERRIGAN\SQL01。是以,我們需要移動我們的檔案,從存儲我們備份檔案的路徑,到我們想去使用的新路徑。在這個例子中,我們隻想從預設執行個體的預設資料目錄,移動到命名執行個體KERRIGAN\SQL01的資料目錄。我們從檔案清單的原始資料和日志檔案擷取完整的路徑,使用我們想去恢複到的新位置來替代完整路徑。在下面片段中的高亮代碼顯示了如何修改位置:

$relocateFileList = @()

$relocatePath = "C:\Program Files\Microsoft SQL Server\MSSQL11.SQL01\

MSSQL\DATA"

#we are putting this in an array in case we have

#multiple data and logfiles associated with the database

foreach($file in $fileList)

{

#restore to different instance

#replace default directory path for both

$relocateFile = Join-Path $relocatePath (Split-Path $file.

PhysicalName -Leaf)

$relocateFileList += New-Object Microsoft.SqlServer.Management.

Smo.RelocateFile($file.LogicalName, $relocateFile)

}

注意,我們的數組包含了Microsoft.SqlServer.Management.Smo.RelocateFile對象,将包含我們資料庫檔案的邏輯和(重定位的)實體檔案名。

$relocateFileList += New-Object Microsoft.SqlServer.Management.Smo.
RelocateFile($file.LogicalName, $relocateFile)      

為了恢複我們的資料庫,我們隻使用Backup-SqlDatabase cmdlet。這裡有一對很重要的選項,像RelocateFile和NoRecovery。

#restore the full backup to the new instance name
#note we have a NoRecovery option, because we have
#additional files to restore
Restore-SqlDatabase `
-ReplaceDatabase `
-ServerInstance $instanceName `
-Database $restoredDBName `
-BackupFile $fullBackupFile.FullName `
-RelocateFile $relocateFileList `
-NoRecovery      

步驟3 – 在完整備份恢複完後,使用NORECOVERY恢複最後的好的差異備份

一旦完整備份恢複,你可以添加最後的好的差異備份跟随着完整備份。他并不是一個內建的過程,因為在這點上我們已經恢複了基礎資料庫并重定位了我們的檔案。我們需要使用NORECOVERY恢複差異備份,阻止未送出的事務被復原:

#using PowerShell V2 Where syntax

$diffBackupFile =

Get-ChildItem $backupfilefolder -Filter "*Diff*" |

Where {$_.LastWriteTime -ge $fullBackupFile.LastWriteTime} |

Sort -Property LastWriteTime -Descending |

Select -Last 1

Restore-SqlDatabase `

-ReplaceDatabase `

-ServerInstance $instanceName `

-Database $restoreddbname `

-BackupFile $diffBackupFile.FullName `

-NoRecovery

注意,在你的環境中,你可能有,也可能沒有一個差異備份檔案。如果沒有,不用擔心,它不會影響到你的可恢複性,隻要所有的事務日志檔案可用于恢複。

步驟4 – 在恢複差異備份後恢複事務日志

在我們恢複了差異備份檔案,我們開始恢複我們的事務日志備份檔案。這些事務日志備份檔案應該是跟随着你的差異備份。你可能需要,或不需要跟随着差異備份的日志檔案的完整集合。如果你需要恢複直到資料庫故障點,你将需要恢複所有的事務日志備份包括尾日志備份。如果不是,你隻需要知道你想恢複的事件點的備份檔案。

對于我們的方案,我們識别出我們想去恢複的最後日志備份檔案。這很重要,因為我們需要知道如何使用PointInTime參數,當我們使用這個特定的事務日志備份檔案時。

#identify the last txn log backup file we need to restore
#we need this so we can specify point in time
$lastTxnFileName = "AdventureWorks2008R2_Txn_201507270252"
$lastTxnBackupFile =
Get-ChildItem $backupfilefolder -Filter "*$lastTxnFileName*"      

對于所有其他的事務日志備份檔案,我們周遊所有的備份目錄,恢複所有的在最後差異備份後的,在我們想去恢複的最後事務日志備份檔案之前的所有.txn檔案。我們也需要通過WriteTime參數來排序這些檔案,以便于我們依次恢複它們到資料庫。注意,我們需要使用NORECOVERY恢複所有的這些檔案。

foreach ($txnBackup in Get-ChildItem $backupfilefolder -Filter "*Txn*"

|

Where {$_.LastWriteTime -ge $diffBackupFile.LastWriteTime -and

$_.LastWriteTime -lt $lastTxnBackupFile.LastWriteTime} |

Sort -Property LastWriteTime)

{

Restore-SqlDatabase `

-ReplaceDatabase `

-ServerInstance $instanceName `

-Database $restoreddbname `

-BackupFile $txnBackup.FullName `

-NoRecovery

}

一旦所有的這些檔案恢複後,然後我們準備恢複最後的事務日志檔案。一旦這個檔案恢複,資料庫需要可通路,所有的未送出事務需要被復原。

有兩個方法去實作。第一個方法,我們在這個方案中使用的,是去使用ToPointInTime參數恢複最後的檔案,并且不使用NoRecovery參數。

Restore-SqlDatabase `
-ReplaceDatabase `
-ServerInstance $instanceName `
-Database $restoreddbname `
-BackupFile $lastTxnBackupFile.FullName `
-ToPointInTime "2015-07-27 02:51:59"      

另一個方法是恢複最後的事務日志檔案,也使用NoRecovery,但是在最後添加另一個指令,使用WITH RECOVERY恢複該資料庫。實際上,一直以來使用NORECOVERY恢複所有需要的事務日志備份檔案更為安全。它更安全,是因為當我們突然使用WITH RECOVERY恢複一個檔案,糾正它的唯一方法是重做整個恢複過程。這可能對于小型資料庫沒多大關系,但是對于大型資料庫可能非常消耗時間。

一旦我們确認所有需要的檔案已經被恢複,我們就可以使用WITH RECOVERY來恢複資料庫。在我們的方案中,一個方法是使用T-SQL語句,并傳遞該語句到Invoke-Sqlcmd:

#get the database out of Restoring state
#make the database accessible
$sql = "RESTORE DATABASE $restoreddbname WITH RECOVERY"
Invoke-Sqlcmd -ServerInstance $instanceName -Query $sql      

RESTORE DATABASE命名使得我們的資料庫從一個正在恢複中的狀态,到可通路和以備使用狀态。RESTORE命名復原了所有未完成的事務,并讓資料庫以備使用。

二、 使用PowerShell調用MTools分析MongoDB性能并發送郵件

在MongoDB日常運維中,經常需要檢視連接配接數的趨勢圖、慢查詢、Overflow語句、連接配接來源。任何資料庫的DBA都應該對資料庫情況進行定期的巡檢,以清楚了解資料庫的運作情況,健康狀況,隐患等等。MTools工具應運而生,它帶給DBA極大地幫助。

Mtools簡介

Mtools是由MongoDB Inc 官方工程師所寫,設計之初是為了友善自己的工作,但是随着MongoDB使用者的增加,越來越多的朋友也開始使用Mtools,也越來越感受到Mtools帶來的便捷。

Github位址如下:

Mtools github位址

Mtools主要有以下元件:

  • mlogfilter
  • mloginfo
  • mplotqueries
  • mlogvis
  • mlaunch
  • mgenerate

首先,我們來簡單介紹下 mlogfilter,mloginfo和mplotqueries。

mlogfileter我們可以簡單了解為日志的過濾器,參數如下:

mlogfilter [-h] [--version] logfile [logfile ...]

[--verbose] [--shorten [LENGTH]]

[--human] [--exclude] [--json]

[--timestamp-format {ctime-pre2.4, ctime, iso8601-utc, iso8601-local}]

[--markers MARKERS [MARKERS ...]] [--timezone N [N ...]]

[--namespace NS] [--operation OP] [--thread THREAD]

[--slow [SLOW]] [--fast [FAST]] [--scan]

[--word WORD [WORD ...]]

[--from FROM [FROM ...]] [--to TO [TO ...]]

示例:

通過mlogfilter查詢日志中某個表的slow log(超過100ms的)

mlogfilter --namespace xxx.xx --slow 100 mongod.log-20160611      

mloginfo可以過濾總結出slow query的情況,以及為日志中各類最常常出現情況進行統計,參數如下:

mloginfo [-h] [--version] logfile

[--verbose]

[--queries] [--restarts] [--distinct] [--connections] [--rsstate]

示例:

通過mloginfo統計日志中connections的來源情況

mloginfo mongod.log-20160611 --connections      

mplotqueries相對複雜一些,功能是可以根據需求畫圖,以便更直覺的找出問題或者隐患所在,參數如下:

mplotqueries [-h] [--version] logfile [logfile ...]

[--group GROUP]

[--logscale]

[--type {nscanned/n,rsstate,connchurn,durline,histogram,range,scatter,event} ]

[--overlay [ {add,list,reset} ]]

[additional plot type parameters]

示例:

通過mplotqueries對連接配接情況進行分析,時間塊機關1800(30min)

mplotqueries mongod.log-20160611 --type connchurn --bucketsize 1800 --output-file 01-9.png      

解決方案

筆者将在Windows上安裝MTools工具來分析mongod.log日志,然後通過Powershell發送郵件。

1. 将Windows備份機目錄挂載到MongoDB本地目錄下,将LogRotate切換後的最新一個日志拷貝到備份目錄。

參考博文:《在Linux下挂載Windows系統共享目錄》

2. 在Windows伺服器上安裝Mtools。

參考博文:《在64位Windows Server 2008 R2上安裝mtools》

3. 編寫PowerShell腳本,通過Mtools分析日志檔案,并發送郵件。

Github源碼位址:https://github.com/UltraSQL/MongoDBDailyReport.git

使用方法:

a) 将DBA子產品放到相應的Modules\DBA目錄下。

b) 在配置檔案中加載子產品:Import-Module DBA -Force。

c) 建立任務計劃,定時執行該MTools.ps1腳本。  

使用Powershell實作資料庫自動化運維