监控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分钟运行这个调度。

 

这个进程主要是为了帮助处理事务复制的间歇性停工。使用复制监视器按期监视复制进程仍然重要。这个进程只是为了阻止下班时间的电话骚扰,只须要启动下分发代理做业就能够修复。

相关文章
相关标签/搜索