Condividi tramite


事务复制会话 (四)

四、分发代理读者线程延迟

与日志读取器一样,分发代理也是有两个会话同步进行。从分发服务器到订阅服务器移动行的延迟在两种会话中都可能会发生。读者线程会话负责查询分发数据库,从而得到要应用到订阅方的事务列表。它首先查询订阅数据库的元数据表,找到最后一次成功复制的事务。然后,读者线程执行sp_Msget_repl_commands开始从分发数据库读数据行并将这些行迁移至内存中的队列。当行进入队列,分发代理写者线程会话开始将这些行写到订阅服务器。和日志读取器类似,添加分发代理 –OUTPUT参数,会得到对分发代理性能的一个很好的概览。另外,最新版本的SQL Server会在分发代理历史表Msdistribution_history中自动记录这些信息。

  

可能的原因:大批量的事务

延迟问题通常在一系列事务尝试向订阅方移动大批的命令时产生。下面的查询显示了在分发数据库中命令的所有行数以及索引统计数据的信息。

 

--ROW COUNTS

--寻找大的行数值(>1百万),从而说明清除过程没有在运行

--或者有大的待完成事务

SELECT name, rowcnt, STATS_DATE (id, indid) as 'Last Update Stats'

FROM distribution.dbo.sysindexes

WHERE name IN('ucMSrepl_transactions', 'ucMSrepl_commands')

 

行数是预期的值,还是包含了上百万行?行数多(>1百万)可能说明一个大的事务正在运行或者清除作业没有在运行。

 

在进行延迟的性能故障排除时,按天计算命令的数量来查看未处理的命令可能会发现有一些有帮助的模式。下面的语句可以查询到存储在分发数据库中命令的拆分信息。

 

--检查临时表中与那些事务数目相关的时间信息

select t.publisher_database_id, t.xact_seqno,

      max(t.entry_time) as EntryTime, count(c.xact_seqno) as CommandCount

into #results

FROM MSrepl_commands c with (nolock)

LEFT JOIN msrepl_transactions t with (nolock)

      on t.publisher_database_id = c.publisher_database_id

      and t.xact_seqno = c.xact_seqno

GROUP BY t.publisher_database_id, t.xact_seqno

 

--显示每小时以及每天的命令数

SELECT publisher_database_id

      ,datepart(year, EntryTime) as Year

      ,datepart(month, EntryTime) as Month

      ,datepart(day, EntryTime) as Day

      ,datepart(hh, EntryTime) as Hour

      --,datepart(mi, EntryTime) as Minute

      ,sum(CommandCount) as CommandCountPerTimeUnit

FROM #results

GROUP BY publisher_database_id

      ,datepart(year, EntryTime)

      ,datepart(month, EntryTime)

      ,datepart(day, EntryTime)

      ,datepart(hh, EntryTime)

      --,datepart(mi, EntryTime)

--order by publisher_database_id, sum(CommandCount) Desc

ORDER BY publisher_database_id, Month, Day, Hour

 

在下面的输出示例中,由于发布表的大量更新,一大批的事务正在被复制,导致分发代理变慢。

 

publisher_database_id Year Month Day Hour CommandCountPerTimeUnit

--------------------- ----------- ----------- ----------- ----------- -----------------------

2 2009 5 14 10 132

2 2009 5 14 11 656

2 2009 5 14 12 880

2 2009 5 14 13 4379

2 2009 5 14 14 152

2 2009 5 14 15 1478

2 2009 5 14 20 161

2 2009 5 14 21 145

2 2009 5 15 6 1700

2 2009 5 15 7 3672

2 2009 5 15 8 6266

2 2009 5 15 9 329

2 2009 5 15 10 5678715

2 2009 5 15 11 5637959

2 2009 5 15 12 5281732

2 2009 5 15 13 5020950

2 2009 5 15 14 1252

2 2009 5 16 11 732

2 2009 5 16 12 178

2 2009 5 16 13 725

2 2009 5 16 14 186

2 2009 5 16 16 72

 

解决方法:

分发数据库上的分发代理读者线程对大批事务有很大的IO需求。快速的磁盘以及将事务日志和数据库文件分别存放在不同的磁盘上会帮助改进IO性能。如果问题持续发生,请考虑发布存储过程执行而不是其结果行。

(关于在事务复制中发布存储过程执行,请参考:https://msdn.microsoft.com/zh-cn/library/ms152754(SQL.90).aspx

 

故障排除读者线程: 当从分发数据库中获取行时,读者线程中的分发代理可能出现延迟。建议先查看代理历史日志或在-OUTPUT结果中查看每五分钟产生的代理统计信息。

 

**************** STATISTICS SINCE AGENT STARTED ******************

03-22-2009 09:55:19

 

Total Run Time (ms) : 59189906 Total Work Time : 492741

Total Num Trans : 5 Num Trans/Sec : 0.01

Total Num Cmds : 5 Num Cmds/Sec : 0.01

Total Idle Time : 58660470

 

Writer Thread Stats

  Total Number of Retries : 0

  Time Spent on Exec : 0

  Time Spent on Commits (ms): 16 Commits/Sec : 0.05

  Time to Apply Cmds (ms) : 0 Cmds/Sec : 0.00

  Time Cmd Queue Empty (ms) : 528717 Empty Q Waits > 10ms: 5

  Total Time Request Blk(ms): 59189187

  P2P Work Time (ms) : 0 P2P Cmds Skipped : 0

 

Reader Thread Stats

  Calls to Retrieve Cmds : 11738

  Time to Retrieve Cmds (ms): 492741 Cmds/Sec : 0.01

  Time Cmd Queue Full (ms) : 0 Full Q Waits > 10ms : 0

******************************************************************

 

以上信息是取自分发代理日志的-OUTPUT输出,可以发现在代理停止工作之前,100%的时间都花在从分发数据库读取行的过程。

 

代理日志同时记录了事件完成的时间。在下面的例子中,从分发代理在订阅方查询待复制的开始事务,到它从分发数据库获取一个批的事务之间,分发代理查询花掉了4分钟的时间。这个延迟是由读者线程等待分发数据库返回命令导致的。

 

--分发代理-OUTPUT输出显示的读者线程延迟示例

--元数据订阅读操作完成

2009-05-16 01:26:49.229 OLE DB Distributor 'SQL380': {call sp_MSget_subscription_guid(11)}

--4分钟后命令复制的读操作完成

2009-05-16 01:29:31.401 sp_MSget_repl_commands timestamp  value is: 0x0x0000081a00000045000400000000

--读者线程等待分发数据库返回命令

 

要测试读的执行时间,我们可以运行sp_Msget_repl_commands,从而查找阻塞、表扫描、超时等信息。

 

  1. 从-OUTPUT输出得到长时间运行的sp_Msget_repl_commands的LSN

 

         2009-05-16 01:29:31.401 sp_MSget_repl_commands timestamp value is: 0x0x0000081a00000045000400000000

 

  1. 从同一个日志中得到Agent_ID

 

  1. 在分发数据库上执行sp_Msget_repl_commands查询

 

     sp_MSget_repl_commands @agent_id=11, @last_xact_seqno=0x0000081a00000045000400000000

 

该命令需要执行多久?有没有被阻塞?是否有大量的命令要被返回?

 

我们也可以利用下面的语句,从订阅端的数据库获得上一次成功执行的事务。Transaction_timestamp值包含了用于存储过程sp_Msget_repl_commands的LSN值。

 

USE <subscriber db>

--获得上一个成功执行的事务

--每个发布有多个行说明有并行的分发流。

SELECT publisher,publication, distribution_agent,transaction_timestamp

FROM dbo.MSreplication_subscriptions

 

要获得更多细节信息,使用下面的查询检查执行计划:

 

USE distribution

GO

 

DBCC FREEPROCCACHE

DBCC DROPCLEANBUFFERS

 

SET STATISTICS PROFILE ON

SET STATISTICS IO ON

SET STATISTICS TIME ON

 

sp_MSget_repl_commands @agent_id=11, @last_xact_seqno=0x0000081a00000045000400000000

 

SET STATISTICS PROFILE OFF

SET STATISTICS IO OFF

SET STATISTICS TIME OFF

 

在SQL 2005/2008中,我们还可以从DMV中得到缓存的执行计划。

 

--dm_exec_query_stats for sp_MSget_repl_commands

-- by top total_worker_time

SELECT TOP 25

  st.text, qp.query_plan,

    (qs.total_logical_reads/qs.execution_count) as avg_logical_reads,

    (qs.total_logical_writes/qs.execution_count) as avg_logical_writes,

    (qs.total_physical_reads/qs.execution_count) as avg_phys_reads,

  qs.*

FROM sys.dm_exec_query_stats as qs

         CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as st

         CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) as qp

WHERE st.text like 'CREATE PROCEDURE sys.sp_MSget_repl_commands%'

  ORDER BY qs.total_worker_time DESC

Go

 

可能原因:表统计信息

当读者线程运行时间长并包含很多小批事务时,可能说明复制系统表需要更新统计信息。如果分发数据库的AutoUpdateStatistics被禁用了,表统计信息就可能会过期。

 

-- 查看复制表的表统计信息,尤其是'Updated'(被更新)的日期

DBCC SHOW_STATISTICS(MSrepl_commands,ucMSrepl_commands)

DBCC SHOW_STATISTICS(MSrepl_transactions,ucMSrepl_transactions)

 

-- 获得分发数据库的设置

-- 查找IsAutoUpdateStatistics

sp_helpdb distribution

 

--或(返回 1表示IsAutoUpdateStatistics为“是”)

select DATABASEPROPERTYEX('distribution','IsAutoUpdateStatistics')

 

解决方法:

 

--更新复制表的统计信息

UPDATE STATISTICS MSrepl_transactions

   WITH FULLSCAN

 

UPDATE STATISTICS MSrepl_commands

   WITH FULLSCAN

 

解决方法:

使用SQL内部工具,确定分发代理读者线程在分发数据库上没有被阻塞。