監控SQL Server事務複製

監控SQL Server事務複製html

 

一般,咱們可使用SSMS的複製監視器來監控複製。但咱們不能24小時盯着看,得使用自動化的方式來監控它。微軟在distribution數據庫提供了系統存儲過程dbo.sp_replmonitorsubscriptionpendingcmds,用於返回訂閱上等待的命令數,以及須要投遞全部這些命令到訂閱者的時間的預估。我建立了一個每10分鐘運行的做業,保存狀態的歷史記錄數據到一個表,數據保留14天。sql

 

這個表在訂閱者服務器的DBA數據庫建立,代碼以下:數據庫

CREATE TABLE dbo.Replication_Qu_History(
Subscriber_db varchar(50) NOT NULL,
Records_In_Que numeric(18, 0) NULL,
CatchUpTime numeric(18, 0) NULL,
LogDate datetime NOT NULL,
CONSTRAINT PK_EPR_Replication_Que_History PRIMARY KEY CLUSTERED
(
Subscriber_db ASC, LogDate DESC
) ON PRIMARY
GO

 

表裏數據經過監控存儲過程生成,能夠經過歷史數據查找問題。然而更須要監控如今發生了什麼。服務器

 

有三個事能夠幫助肯定複製的健康狀況。ide

1. 複製相關做業的狀態。this

2. 延時,尤爲是計數器Dist:Delivery Latency衡量的分發延時。代理

3. 訂閱等待的大量未執行命令數。rest

 

我將注意力集中在了分發延時,由於從過去的經驗告訴我,相比日誌讀取延時,分發延時的問題更加突出。多數時候,分發延時是因爲事務量的增長。例如,在發佈數據的一個大表上作索引重建,會致使事務日誌量的驟然增長,結果致使比正常狀況更多的數據須要被複制。日誌

 

若是有大量的命令等待被分發,有時候多是分發代理做業沒有運行。另外一方面,有時候是這個做業在運行,可是沒有跟上。經過重啓代理,做業開始處理未執行的命令。orm

 

開始以前,咱們須要知道複製的信息,像發佈者和訂閱者的名字、分發代理做業的名字等等。微軟在分發數據庫中提供了一些存儲過程來收集這些信息。筆者的分發數據庫和訂閱者數據庫在一塊兒,因此相比在不一樣的服務器,腳本更加簡單些。

1. 首先,在分發數據庫執行sp_replmonitorhelppublisher獲取全部發布者的監控信息。

2. 而後,在分發數據庫執行sp_replmonitorhelppublication返回全部發布的監控信息。

3. 最後,執行sp_replmonitorhelpsubscription返回全部訂閱的監控信息。

 

這個信息包含一些延時指標數據,因此執行這個存儲過程後,我已經有些關鍵信息了。

 

如下是用於收集信息的代碼:

DECLARE @cmd NVARCHAR(max)
DECLARE @publisher SYSNAME, @publisher_db SYSNAME, @publication SYSNAME, @pubtype INT
DECLARE @subscriber SYSNAME, @subscriber_db SYSNAME, @subtype INT
DECLARE @cmdcount INT, @processtime INT
DECLARE @ParmDefinition NVARCHAR(500)
DECLARE @JobName SYSNAME
DECLARE @minutes INT, @threshold INT, @maxCommands INT, @mail CHAR(1) = 'N'
SET @minutes = 60 --> Define how many minutes latency before you would like to be notified
SET @maxCommands = 80000 ---> change this to represent the max number of outstanding commands to be proceduresed before notification
SET @threshold = @minutes * 60
SELECT * INTO #PublisherInfo
FROM OPENROWSET('SQLOLEDB', 'SERVER=(LOCAL);TRUSTED_CONNECTION=YES;'
, 'SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelppublisher')
SELECT @publisher = publisher FROM #PublisherInfo
SET @cmd = 'SELECT * INTO ##PublicationInfo FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelppublication @publisher='
+ @publisher + ''')'
--select @cmd
EXEC sp_executesql @cmd
SELECT @publisher_db=publisher_db, @publication=publication, @pubtype=publication_type FROM ##PublicationInfo
SET @cmd = 'SELECT * INTO ##SubscriptionInfo FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelpsubscription @publisher='
+ @publisher + ',@publication_type=' + CONVERT(CHAR(1),@pubtype) + ''')'
--select @cmd
EXEC sp_executesql @cmd
ALTER TABLE ##SubscriptionInfo
ADD PendingCmdCount INT NULL,
EstimatedProcessTime INT NULL

 

在知道了發佈者和訂閱者的基本信息後,而後,檢查分發做業的狀態。它們應該一直在運行。若是沒有運行,你要啓動它。若是我須要重啓一個做業,我會設置標識強制發送郵件告警。

 

我不是爲了發送郵件告警而已,是爲了檢查全部訂閱的狀態。若是設置的數據超過了設置的閾值,將會觸發郵件告警。我用一個遊標遍歷全部的訂閱,這是最容易的收集信息的方法。我將這個信息做爲其餘存儲過程的參數,用於肯定分發代理是否正在運行,還能夠重啓代理。

DECLARE cur_sub CURSOR READ_ONLY FOR
SELECT @publisher, s.publisher_db, s.publication, s.subscriber, s.subscriber_db, s.subtype, s.distribution_agentname
FROM ##SubscriptionInfo s
OPEN cur_sub
FETCH NEXT FROM cur_sub INTO @publisher, @publisher_db, @publication, @subscriber, @subscriber_db, @subtype, @JobName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @cmd = 'SELECT @cmdcount=pendingcmdcount, @processtime=estimatedprocesstime FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorsubscriptionpendingcmds @publisher=' + @publisher
+ ',@publisher_db=' + @publisher_db + ',@publication=' + @publication
+ ',@subscriber=' + @subscriber + ',@subscriber_db=' + @subscriber_db
+ ',@subscription_type=' + CONVERT(CHAR(1),@subtype) + ';' + ''')'
SET @ParmDefinition = N'@cmdcount INT OUTPUT,
@processtime INT OUTPUT'
--select @cmd
EXEC sp_executesql @cmd,@ParmDefinition,@cmdcount OUTPUT, @processtime OUTPUT
UPDATE ##SubscriptionInfo
SET PendingCmdCount = @cmdcount
, EstimatedProcessTime = @processtime
WHERE subscriber_db = @subscriber_db
INSERT INTO DBA.dbo.Replication_Que_History
VALUES(@subscriber_db, @cmdcount, @processtime, GETDATE())
-- find out if the distribution job with the high number of outstanding commands running or not
-- if it is running then sometimes stopping and starting the agent fixes the issue
IF EXISTS(SELECT * FROM tempdb.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '##JobInfo%')
DROP TABLE ##JobInfo
SET @cmd = 'SELECT * INTO ##JobInfo FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC msdb.dbo.sp_help_job @job_name='''''
+ @JobName + ''''',@job_aspect=''''JOB'''''')'
EXEC sp_executesql @cmd
IF @cmdcount > @maxCommands OR (@processtime > @threshold AND @cmdcount > 0)
BEGIN
IF (SELECT current_execution_status FROM ##JobInfo) = 1 -- This means job is currently executing so stop/start it
BEGIN
EXEC distribution.dbo.sp_MSstopdistribution_agent
@publisher = @publisher
, @publisher_db = @publisher_db
, @publication = @publication
, @subscriber = @subscriber
, @subscriber_db = @subscriber_db
WAITFOR DELAY '00:00:05' ---- 5 Second Delay
SET @mail = 'Y'
END
END
--SELECT name, current_execution_status FROM ##JobInfo
IF (SELECT current_execution_status FROM ##JobInfo) <> 1 -- if the job is not running start it
BEGIN
EXEC distribution.dbo.sp_MSstartdistribution_agent
@publisher = @publisher
, @publisher_db = @publisher_db
, @publication = @publication
, @subscriber = @subscriber
, @subscriber_db = @subscriber_db
SET @mail = 'Y' -- Send email if job has stopped and needed to be restarted
END
DROP TABLE ##JobInfo
FETCH NEXT FROM cur_sub INTO @publisher, @publisher_db, @publication, @subscriber, @subscriber_db, @subtype, @JobName
END
CLOSE cur_sub
DEALLOCATE cur_sub

 

運行sp_replmonitorsubscriptionpendingcmds收集未執行的命令和預計跟上的時間。

 

這是我想在歷史表裏存儲的信息,所以我能夠了解到複製執行得怎樣了。

 

咱們須要肯定一個能夠接受的延時閾值。我這裏使用6分鐘,意思是,若是複製的數據庫落後於發佈數據庫多餘6分鐘,將受到告警。還要肯定未分發命令的最大數量。若是這個數量向上波動,可能會有問題。你能夠選擇在讓這個數字設置爲多高時才採起行動。我選擇讓這個系統有80000個未分發命令。

 

在讓複製隊列檢查做業運行了兩週後,我獲取了這些數據。確保這些做業像索引重建做業同樣運行。我查看了一段時間未分發命令的最大數量和最大延時,並肯定個人設置值會更大些。我不想由於索引重建做業致使的系統臨時備份而在晚上被叫醒,這是會自動恢復的。

 

如下的代碼須要啓用Ad Hoc Distributed Queries服務器配置選項。假設以前的腳本發現了問題,我建立了發送郵件的腳本。

IF @mail = 'Y'
BEGIN
DECLARE @msg VARCHAR(MAX) = 'Replication on ' + @@SERVERNAME
+ ' may be experiencing some problems. Attempts to restart the distribution agent have been made. '
+ 'If this is not the first message like this that you have received within the last hour, please investigate.'
DECLARE @body NVARCHAR(MAX)
DECLARE @xml1 NVARCHAR(MAX)
DECLARE @tab1 NVARCHAR(MAX)
DECLARE @xml2 NVARCHAR(MAX)
DECLARE @tab2 NVARCHAR(MAX)
SET @xml1 = CAST(( SELECT subscriber AS 'td','',subscriber_db AS 'td','',
latency AS 'td','', PendingCmdCount AS 'td','', EstimatedProcessTime AS 'td'
FROM ##SubscriptionInfo s
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET @tab1 ='<html><body><H4>Subscription Information </H4>
<table border = 1> <tr>
<th> Subscriber </th> <th> Subscriber Database </th> <th> Latency(seconds)</th>
<th> Undistributed Commands </th> <th> Estimated Catch Up Time</th></tr>'
-- this command gives us the last 10 measurements of latency for each subscriber
SET @xml2 = CAST(( SELECT s.Subscriber_db AS 'td','', s.Records_In_Que AS 'td','', s.CatchUpTime AS 'td','', CONVERT(CHAR(22),LogDate, 100) AS 'td'
FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY subscriber_db ORDER BY Logdate DESC ) AS 'RowNumber',
subscriber_db
, Records_In_Que
, CatchUpTime
, Logdate
FROM DBA.dbo.Replication_Que_History
) s
WHERE RowNumber <= 8
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET @tab2 ='<br><br><H4>Historical Latency Information </H4>
<table border = 1>
<tr>
<th>Subscriber</th> <th>Undistributed Commands</th> <th> Catch Up Time </th> <th> Date\Time </th></tr>'
SET @body = @msg + @tab1 + @xml1 + '</table>'
+ @tab2 + @xml2 + '</body></html>'
DECLARE @to NVARCHAR(200)
SELECT @to = '' -- INSERT YOUR EMAIL ADDRESS HERE
EXEC msdb.dbo.sp_send_dbmail
@body = @body,
@body_format ='HTML',
@recipients = @to,
@subject = 'Possible Replication Problem' ;
END
DROP TABLE #PublisherInfo
DROP TABLE ##PublicationInfo
DROP TABLE ##SubscriptionInfo

 

最後,須要按期刪除複製狀態表的數據,以便數據不會太舊。

DECLARE @delDate datetime = getdate()-10
DELETE FROM DBA.dbo.Replication_Que_History
WHERE LogDate < @deldate

 

若是該腳本中配置的任何閾值匹配上,與有問題的計數器的訂閱相關的發佈代理將會重啓,若是已經中止,做業將會啓動。你將會受到該動做的通知郵件。在不少狀況下,重啓分發代理會解決問題,複製又開始工做。若是依然沒有修復這個問題,那麼做業下次運行相同的動做,又收到另外一封郵件。你須要着手檢查下這種狀況。

 

你能夠在你的告警系統裏調用第3個腳本,當任何閾值匹配時重啓分發代理做業。或者,運行第1個腳本建立表。建立新的做業,在第1步運行後面3個腳本,而後將第5個腳本放到第2步。我當前每10分鐘運行這個調度。

 

這個進程主要是爲了幫助處理事務複製的間歇性停工。使用複製監視器按期監視複製進程仍然重要。這個進程只是爲了阻止下班時間的電話騷擾,只須要啓動下分發代理做業就能夠修復。

相關文章
相關標籤/搜索