本文接上面兩篇搭建鏡像的文章:
在配置過程中,可能會有一系列的問題,特别是初次搭建。另外,很多正式環境并不能理想化配置,是以這一部分會列出一些常見的、本人搭建過程中也會遇到的一些問題,希望對讀者處理鏡像有所幫助。
常見的問題主要集中在三類:資料庫還原問題、事務日志問題和通信問題。
前面幾篇文章中反複提醒,當還原資料庫到鏡像伺服器時,需要使用NORECOVERY選項,如果沒有使用這個選項,會傳回Error 1416錯誤:
截圖如下:

當出現這種情況時,對于大庫就比較痛苦了,你必須使用NORECOVERY選項重新還原。 說白了,你剛才的還原已經沒用了。并且為了追上主體伺服器的進度,還需要還原一系列的日志備份,注意日志備份同樣需要使用NORECOVERY選項。隻要其中一個沒用到NORECOVERY,必須從頭開始。
事務日志的問題,是前面說過的在搭建鏡像前,要停用所有相關庫的日志備份作業的原因之一。如果在開始鏡像前沒有還原事務日志到鏡像伺服器上,或者在鏡像伺服器上已經還原了完整備份之後,又在主體伺服器上進行了日志備份,卻沒有把備份檔案同步到鏡像伺服器時,會出現下面兩類錯誤:
Error 1412 - The remote copy of database <DatabaseName> has not been rolled forward to a point in time that is encompassed in the local copy of the database log.這種情況是發生在做了一次完整備份,并把備份檔案還原到鏡像庫中,然後還沒開始鏡像配置時又在主體伺服器做了一次日志備份導緻的,解決這種問題,隻需要把日志檔案重新還原到鏡像資料庫即可。
這種情況較為少見,通常出現在配置鏡像過程中,正常的備份作業還在運作,打斷了原有的日志鍊。無論是哪種報錯,保證在主體伺服器的備份種類及次數都應該和鏡像庫還原過程中一樣,并且禁用主體庫的正常備份以免影響日志鍊。
相對于前面兩種問題,通信問題是最難解決的,因為問題可能會有很多種,也可能是很多因素導緻同一個報錯。另外,由于涉及2台甚至3台伺服器,是以問題的偵測難度更大。常見的通信報錯有下面3中:
Error 1418 - The server network address <NetworkAddress> can not be reached or does not exist. Check the network address name and that the ports for the local and remote endpoints are operational. Error 1486 - Database Mirroring Transport is disabled in the endpoint configuration. Error 1456 - The ALTER DATABASE command could not be sent to the remote server instance <NetworkAddress>. The database mirroring configuration was not changed. Verify that the server is connected, and try again.
針對上面三種錯誤,正常步驟如下:
確定SQL Server不是使用Local System 賬号運作。必須使用Network Service或者域賬号運作,除非使用證書驗證。 嘗試Telnet每個需要用到的端口,如果不行,可能需要配置防火牆。 查詢sys.database_mirroring_endpoints目錄視圖,確定鏡像環境中的每個端點的加密和授權方式一緻。 查詢sys.tcp_endpoints目錄視圖,確定資料庫鏡像端點已經啟動,如果沒啟動,可以使用下面語句實作:
根據本人經曆,配置過程的問題很多時候是沒有根據“規定”和“規範”來做,缺少了或者做錯了一些操作導緻的,是以建議讀者仔細研究搭建部分,跟着做基本上就不會有太多問題,等對搭建和使用都有了一定心得之後,再進行擴充使用。
如果你搭建好高可用方案就覺得已經完事的話,後果将非常嚴重。是以本文重點介紹正常的鏡像維護,記住維護工作極其重要。
維護工作主要包含下面幾個部分:
事務日志備份 監控日志檔案大小 SQL Server正常更新 故障轉移後恢複原有主體伺服器 資料庫配置 控制故障轉移 自動故障轉移
鏡像的核心思想就是對事務日志的操作,這也是鏡像必須使用完整恢複模式的原因之一。抛開高可用,任何完整模式下的資料庫如果缺乏維護,日志檔案都會出現非預期增長,最終導緻資料庫變成隻讀。根據拇指定律,核心系統的日志備份監控不宜大于半個小時。如果事務量巨大,甚至需要把備份間隔縮短到15分鐘。
腳本的核心主要有下面幾個:
檢查資料庫狀态:對于鏡像環境中的鏡像角色,是不能聯機的,也就是不能進行備份(包括日志備份)。是以如果要做備份腳本,需要檢查資料庫狀态,可以檢視sys.database_mirroring和sys.databases中的state/state_desc。 檢查恢複模式:簡單模式下不允許進行事務日志備份,是以需要檢查sys.databases中的recovery_model/recovery_model_desc的資訊。 檢查資料庫快照:對于資料庫快照,同樣不能使用備份操作,是以備份時需要檢查sys.databases中的source_database_id,如果不為null,則證明為快照庫,跳過備份操作。 檢查日志傳送:對部署了日志傳送的資料庫進行日志備份會破壞這個功能,是以需要檢查msdb中的dbo.log_shipping_primary_databases表是否存在要備份的資料庫。 檢查系統庫:tempdb和master庫不能進行日志備份,而model庫也通常沒有必要進行日志備份,對于msdb,視乎使用情況決定,如果搭建了複制,會出現distribution庫,這個庫有時候會産生很多的日志,必要時候也應該納入備份政策中。
下面是從網上專家腳本中收集的腳本,讀者如有必要,可以使用,并且進行适當修改,但是作為尊重,請勿以原創釋出,除非你做了大面積的修改:
由于日志備份的頻率較高,是以可能一段時間之後,日志檔案會有很多個,即使個數不多(以追加的方式每次寫入同一個檔案,本人不建議這樣做),日志備份的體積也可能會很大。是以,對過久的日志檔案,應該進行歸檔或者删除。對檔案的歸檔,可以使用sys.xp_delete_file存儲過程實作。下面提供一個删除日志備份檔案的腳本,同樣是收集來的,使用者不要在沒有大面積修改前直接以原創方式釋出:
CREATE PROCEDURE dbo.dba_DeleteLogBackups
-- Name of database, all databases if null
@DBName SYSNAME = NULL ,
-- Location of log backups
@LogBackupLocation NVARCHAR(255) = NULL ,
-- log backup extension
@FileExtension NVARCHAR(3) = 'trn' ,
@Retention INT = 4 , -- days
-- 0 = execute deletion of log backup,
-- 1 = output the code without executing
@Debug BIT = 0
AS
DECLARE @DeleteDate NVARCHAR(19) ,
@BakDir NVARCHAR(255) ,
@Exists INT
DECLARE @FileExists TABLE
(
FileExists INT NOT NULL ,
FileIsDirectory INT NOT NULL ,
ParentDirectoryExists INT NOT NULL
)
SET NOCOUNT ON
SET @DeleteDate = CONVERT(NVARCHAR(19), DATEADD(DAY, -@Retention,
GETDATE()), 126)
IF @DBName IS NOT NULL
BEGIN
IF NOT EXISTS ( SELECT 1
FROM sys.databases
WHERE name = @DBName )
BEGIN
RAISERROR ('The specified database [%s] does not exist.
Please check the name entered or do not supply
a database name if you want to delete the
log backups for all databases.', 16, 1, @DBName);
RETURN;
END
END
IF @LogBackupLocation IS NULL
EXEC xp_instance_regread N'HKEY_LOCAL_MACHINE',
N'Software\Microsoft\MSSQLServer\MSSQLServer',
N'BackupDirectory', @BakDir OUTPUT, 'no_output';
IF @BakDir IS NOT NULL
INSERT INTO @FileExists
EXEC sys.xp_fileexist @BakDir;
SELECT @Exists = ParentDirectoryExists
FROM @FileExists;
IF @Exists = 1
BEGIN
SET @LogBackupLocation = @BakDir + ISNULL('\'
+ @DBName, '');
END
SELECT TOP 1
@BakDir = LEFT(MF.physical_device_name,
LEN(MF.physical_device_name)
- CHARINDEX('\',
REVERSE(MF.physical_device_name)))
FROM msdb.dbo.backupset BS
INNER JOIN msdb.dbo.backupmediafamily MF ON MF.media_set_id = BS.media_set_id
-- log backups first, then differentials, then full backups
ORDER BY BS.type DESC ,
BS.backup_finish_date DESC; -- newest first
DELETE FROM @FileExists
FROM @FileExists
SET @LogBackupLocation = @BakDir;
IF @LogBackupLocation IS NOT NULL
IF RIGHT(@LogBackupLocation, 1) <> '\'
SET @LogBackupLocation = @LogBackupLocation + '\';
ELSE
RAISERROR ('Backup location not specified or not found.', 16, 1);
RETURN;
IF @Debug = 0
EXEC sys.xp_delete_file 0, @LogBackupLocation, @FileExtension,
@DeleteDate, 1;
PRINT 'Exec sys.xp_delete_file 0, ''' + @LogBackupLocation
+ ''', ''' + @FileExtension + ''', ''' + @DeleteDate
+ ''', 1;';
在确定日志檔案已經有正常備份并且實在太大是,收縮日志檔案可能是必須做的事情,但是通常收縮應該是最後一步。這裡包含兩個部分:
識别需要收縮的日志 收縮日志檔案
這裡使用腳本查詢主體伺服器上已經配置為鏡像的庫機器日志檔案大小:
需要提醒一下,對主體庫的日志收縮并不同步到鏡像庫,是以在使用DBCC SHRINKFILE之後,使用ALTER DATABASE XX MODIFY FILE強制主體庫更改日志檔案的大小。以便同步到鏡像庫。
下面是某位MCM提供的進行收縮的腳本,這個腳本主要針對鏡像庫的日志收縮:
在正式環境中,伺服器的作業系統、SQL Server及其他應用程式可能會定期打更新檔,這時候就涉及重新開機機器或者SQL Server的工作,如果搭建了鏡像環境,這個操作就會稍微變得複雜了。因為為了高可用的穩定運作,不僅主體伺服器要打更新檔,鏡像伺服器甚至見證伺服器也要打更新檔。
本部分包含兩個情況:
安裝正常更新檔 更新SQL Server或為SQL Server打更新檔
這裡指的更新檔不是SQL Server的更新檔,在鏡像環境中,如果打了更新檔并且需要重新開機,盡可能避免鏡像環境的所有夥伴伺服器都同時重新開機,畢竟搭建高可用的原因是為了加大業務連續性。
正常做法是:先對一台伺服器打更新檔,重新開機這台伺服器(如果需要重新開機),確定這台機已經正常運作之後,以相同步驟處理下一台。鏡像環境中重新開機順序沒有強制規定,按照實踐,通常是:見證→鏡像→主體伺服器。
針對SQL Server打更新檔,必須更加謹慎,特别是鏡像環境出現了見證伺服器。如果帶有見證伺服器,首先要移除見證伺服器,并在打完更新檔之後把見證伺服器加回去。見證伺服器可以在任何時候進行更新。可以在主體伺服器上運作:
對于運作模式,建議先設為異步模式,然後再打更新檔,在準備Failover時,改為同步模式,使其同步資訊之後再Failover。
在正式打更新檔時,先對鏡像伺服器打更新檔,最主要的原因是可以驗證更新檔是否有效,并且打完之後是否可以安全Failover。當鏡像伺服器已經打完更新檔并且Failover成主體伺服器時,由于更新檔的級别不一緻,SQL Server會挂起鏡像會話,這時候應該盡快對原主體伺服器打更新檔,否則挂起太久會引起很多後患。
在鏡像環境中,人為或者自動故障轉移都會使鏡像伺服器變成主體伺服器,如果這不是你希望的,那麼你就需要想辦法把現在的主體伺服器變回鏡像伺服器,其中一個原因是如果不這樣做,你必須準備一個新的鏡像伺服器以便下一次故障轉移。這裡分兩步介紹:
恢複原有主體伺服器的原因 實作原有主體伺服器的恢複
恢複原有主體伺服器有很多原因,但是比較重要的原因是:鏡像不同步作業、維護計劃、SSIS等。如果是非人為Failover,會導緻這些元件失敗、重試,而有些系統中,這些元件是至關重要的,盡快聯機并切換回原有狀态是首要任務,另外,鏡像伺服器往往性能沒有主體伺服器強,長時間接管業務可能導緻鏡像伺服器面臨壓力。
可以使用這個腳本進行恢複:
資料庫配置中有兩個選項可以用于鏡像,一個是資料庫擁有者(database owner)和Trustworthy 資料庫屬性。當備份一個資料庫是,SQL Server會重置Trustworthy ,這個屬性搞熟SQL Server可以信任這個資料庫中包含的對象。可以使用:ALTER DATABASE [庫名] SET TRUSTWORTHY ON; 來設定。
在還原資料庫或者建立新庫時,SQL Server會把你操作的賬号作為資料庫的擁有者,這可能因為你這個賬号的權限不足以完成你所需的任務而導緻資料庫在運作過程中報錯。可以使用下面語句來檢視資料庫的擁有者:
如果資料庫onwer為null或者不是期望的,可以用下面語句修改:
Failover一般有兩種:自動轉移和手動轉移,手動轉移可以很簡單地使用下面語句實作:
在Failover過程中,你可以會遇到一些問題,畢竟現實總不是太理想的,如果在異步模式下執行Failover指令,會報錯,需要在Failover之前把資料庫設為同步,下面提供一個控制Failover的存儲過程,常見的問題已經在裡面得到處理:
自動故障轉移要考慮幾個問題:1、夥伴伺服器上的作業、維護計劃等或者其他外部資源。2、應用程式依賴的庫是否也同時轉移,不管是否需要。3、賬号
針對這些問題,可以使用Windows Management Instrumentation(WMI)命名空間來發送警告。可以通過SSMS打開,如圖:
然後配置WMI事件:
然後配置響應頁:
最後填寫警告資訊: