镜像切换Logreader Agent报错:分发数据库中可能存在不一致的状态

软件环境:Windows Server 2008 R2 sp1    SQL Server 2008 R2 sp2

架构:ServerA(主体)+ServerB(镜像)+ServerSub(订阅)+ServerDist(分发)

特别注意:ServerDist分发服务器为单机,故开启了Sync_With_Backup选项。

关于Sync_With_Backup:

初衷:简单来说,开启该选项,使得单机分发库在宕机时,利用最近的系统数据库和用户数据库备份,还原到一台新的服务器上,即可使复制继续工作成为可能。

需要注意的是:在分发库开启该选项,意味着发布库的日志只有在分发库被备份后才会截断。

查看该选项是否开启:

     SELECT name,is_sync_with_backup FROM sys.databases WHERE name LIKE ‘distribution%‘

开启该选项:

sp_replicationdboption ‘distribution‘,‘sync with backup‘,‘true‘

场景:

在生产环境中,分发库备份为半小时一次。

将主体从ServerA切换至ServerB时,Logreader Agent报错:

直观上可以看到,Logreader 认为dist_backup_lsn和dist_last_sn不一致,导致其无法继续工作。下面我们实际去看一下这两者到底是否一致。

下面是Logreader、Distribution Agent工作的步骤列表,为了方便,我都贴出来:

Log Reader Agent (logread.exe) – Sequence of Steps1.Calls sp_MSadd_LogReader_History to write to MSLogReader_History – “Starting Agent”2.sp_MShelp_logreader_agentid – Obtain log reader agent specific information for that publication 3.sp_MShelp_profile – Obtains profile information for the Log Reader4.MSadd_logreader_history to write MSlogreader_history – “Initializing” 5.sp_MSget_last_transaction – determine where the log reader agent left off reading the log. 6.Read the transaction log – sp_replcmds 7.Processes the returned commands from the sp_replcmds in batches by calling sp_MSadd_repl_commands 8.Marks this transaction as committed in distribution database by using sp_repldone procedure 9.Adjusts the identity range if necessary and if you are using Automatic Identity Range Management y calling sp_MSpub_adjust_identity 10.Calls sp_MSget_last_transaction to check the last transaction read and stored in MSReplication_transactions table 11.When all transactions are read, LogRead.exe calls sp_MSAdd_logreader_history and writes message to MSLogReader_history “1 transactions with 9 commands were delivered”•Distribution Agent (distrib.exe) - Sequence of Steps1.master.db.sp_msget_jobstate – get the status of the job (if it is already started) 2.sp_Msadd_distribution_history – MSDistribution_history – Starting agent 3.sp_MSSubscription_Status – whether subscription has expired or the snapshot is ready 4.sp_server_info- determines the collation 5.sp_mshelp_subscriber_info – retrieve subscriber information 6.sp_mshelp_subscription_agentid – determine the name of the distribution agent 7.sp_Msadd_distribution_history – Initializing message – Msrepl_distribution_history 8.sp_Msadd_distribution_history – Connecting to Subscriber - Msrepl_distribution_history 9.so_datatype_info – to determine the data type mapping necessary to create the tracking table necessary for the Distribution agent 10.sp_MScheck_subscribe on subscription database – verifies that SQL Server Agent account is in sysadmin and db_owner role in subscription database 11.sp_mscreate_sub_tables on subscriber in subscription database – creates MSSusbcription_agents and MSreplication_subscriptions tables 12.Sp_MSinit_Subscription_agent – updates the Subscription agent information on subscription database 13.Retrieves transaction_timestamp and subscription_guid to determine what Distribution agent has already replicated to the Subscriber. Transaction_timestamp correlates to xact_seqno column in MSReplication_transactions table in distribution database. All values large than the xact_seqno will be replicated 14.If we are doing initial sync, Distribution Agent calls sp_MSupdatelastsyncinfo which updates MSreplication_susbcriptions and MSSusbcription_agents table 15.Starts to retrieve all transactions and their corresponding commands from MSReplication_transactions and MSreplication_commands table where transaction_timestamp column in subscription database < xact_seqno column in MSreplication_transactions table. Applies the transaction using sp_MS_get_repl_commands procedure 16.Issues dynamic SQL to update the MSreplication_subscriptions table with the last delivered transaction ID 17.sp_MSDistribution_history to write the MSrepl_distribution_history table with status message “nn transaction(S) with nn command(s) were delivered”

在步骤5中,我们得知,Logreader用存储过程sp_Msget_last_transaction来定位发布库日志中最后一个被写入到分发库中的事务,我们找到这个存储过程的源码:

CREATE PROCEDURE sys.sp_MSget_last_transaction(    @publisher_id int = NULL,    @publisher_db sysname,    @publisher sysname = NULL,    @max_xact_seqno varbinary(16) = NULL output    ,@for_truncate bit = 0)ASbegin    declare @publisher_database_id int    declare @max_xact_id varbinary(16)    declare @sync_bit int    declare @sync_with_backup bit

    set nocount on

    -- security check    -- only db_owner can execute this

    if (is_member (‘db_owner‘) != 1)    begin        raiserror(14260, 16, -1)        return (1)    end

    SELECT @sync_bit = 32

    if @publisher_id is NULL        select @publisher_id = srvid from master.dbo.sysservers where            UPPER(srvname) = UPPER(@publisher)

    -- Get publisher database id.    SELECT @publisher_database_id = id from MSpublisher_databases where publisher_id = @publisher_id and        publisher_db = @publisher_db

    if exists ( select * from master.dbo.sysdatabases where            name = db_name() and            category & @sync_bit = 0)        select @sync_with_backup = 0    else        select @sync_with_backup = 1

    if @for_truncate = 0    begin        select top 1 @max_xact_id = rt.xact_id, @max_xact_seqno = rt.xact_seqno          from          MSrepl_transactions rt          where             rt.publisher_database_id = @publisher_database_id and             not xact_id = 0x0             order by xact_seqno desc    end    -- If (1) requesting truncate lsn (distbackuplsn), (2) sync with backup is set    -- query the values from MSrepl_backup_lsn    else if    @sync_with_backup = 1    begin        -- Get the last backed up lsn if available.        select top 1 @max_xact_id = valid_xact_id, @max_xact_seqno = valid_xact_seqno          from          MSrepl_backup_lsns          where             publisher_database_id = @publisher_database_id    end

    -- If @publisher is not null, we are calling this sp from sp_replrestart    -- Don‘t return result set.    if @publisher is null        select @max_xact_id, @max_xact_seqno, @publisher_database_id            -- Don‘t return any result when requsting a truncate lsn and            -- the database is not in ‘sync with backup‘ mode, which signal the            -- distribution agent to use last dist lsn to call sp_repldone.            where not (@sync_with_backup = 0 and @for_truncate = 1)end

存储过程中,代码

else if    @sync_with_backup = 1begin    -- Get the last backed up lsn if available.    select top 1 @max_xact_id = valid_xact_id, @max_xact_seqno = valid_xact_seqno      from      MSrepl_backup_lsns      where         publisher_database_id = @publisher_database_idend

可见,报错中的dist_backup_lsn取自表MSrepl_backup_lsns

     我们来查询一下该表:

select * from MSrepl_backup_lsns

列valid_xact_id为最后备份的事务ID,valid_xact_seqno为该事务中的LSN,即valid_xact_seqno为报错中显示的dist_backup_lsn,(请勿与图中贴出的错误进行对比,上图仅为错误示例截图)。

我们再去分发库MSrepl_transactions表中找到当前已经被读取到分发库的最大事务的LSN

SELECT TOP 1 * from     [dbo].[MSrepl_transactions] as twhere t.publisher_database_id = 5ORDER BY t.entry_time DESC

表中给出的最大事务号是:0x00000026000001B60004,而上面dist_backup_lsn为:0x0002030D00003E7A0004,果然不一致。

但是我们细想,MSrepl_backup_lsns表中的记录半小时才更新一次(跟随分发库备份频率),而MSrepl_transactions中的记录是实时变化的,他们两者在比较繁忙的OLTP系统中是不太可能一致的!!但是为什么在切换的时候会去检查一致不一致?而我如果不切换,只将Logreader重启,它是不会去检查的。

所以我想:这个微软也许最后给我的答案就是他们常用的“By Design”。

先不说这个,我们来继续找一下到底是哪个存储过程在对比这两者,先找到该错误信息所属的MessageID:

SELECT * FROM sys.messages WHERE text LIKE ‘%分发数据库中可能存在%‘

然后我们去系统表中寻找使用了这个message_id的系统存储过程:

SELECT b.name,a.definition FROM sys.all_sql_modules  a    INNER JOIN sys.all_objects b ON b.object_id = a.object_idWHERE a.definition LIKE ‘%18846%‘

Oh,shit!  线索中断了,我们找不到一个系统存储过程使用了该message_id,所以这个判断应该是在系统扩展存储过程中。

总结:说到现在,不知道各位看官是否看明白,当你在分发库开启了sync_with_backup选项,在进行镜像切换时,如果出现:

分发数据库中可能存在不一致的状态: dist_backup_lsn {00020191:000022ff:0004},dist_last_lsn {00020191:00002435:0004}

。请执行"sp_repldone NULL, NULL, 0, 0, 1",然后执行sp_replflush。请重新初始化对发布的所有订阅。

这样的错误,请不要慌张,执行分发库备份作业即可。

镜像切换Logreader Agent报错:分发数据库中可能存在不一致的状态

时间: 2024-10-31 18:01:10

镜像切换Logreader Agent报错:分发数据库中可能存在不一致的状态的相关文章

CloudStack:导入网络镜像的一次报错

1.故障描述 通过"注册ISO"功能导入阿里云的一个镜像,出现如下报错信息: 查看资源域的信息: 2.查看日志 Jan 17 06:25:01 cloudstack-new-02 server: WARN  [o.a.c.alerts] (RemoteHostEndPoint-1:ctx-6d1f1c4f) (logid:9193f818)  alertType:: 28 // dataCenterId:: 1 // podId:: null // clusterId:: null /

8.3 sikuli 集成进eclipse 报错:eclipse中运行提示 Win32Util.dll: Can&#39;t load 32-bit .dll on a AMD 64 bit platform

sikuli运行出现问题:Win32Util.dll: Can't load 32-bit .dll on a AMD 64 bit platform 在64位平台上无法加载32位的dll文件 解决办法:将该工程设置成JDK为32位的 之前eclipse中默认加载的是64位jdk .但由于eclipse中TESTNG需要用到1.7 jdk .为保证在解决问题后,testNG还能正常使用.所以原博主是下了个32位  1.7 jdk,将该工程设置成这个就ok了 解决问题启发来自: http://ww

vs报错“以下文件中的行尾不一致,是否将行尾标准化”

vs报错“以下文件中的行尾不一致,是否将行尾标准化” 分析: 通过读取源文件,发现换行都使用的是“\n”   Windows和Unix不同的标准引起的...即“回车”和“换行”的问题... 符号  ASCII码 意义 \n 10 换行NL \r 13 回车CR 在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符.但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符.要是在这0.2秒里面,又有新的字符传过来,那么

Hibernate 查询结果与数据库中的数据不一致

hibernate的bug: https://hibernate.atlassian.net/browse/HHH-4956 解决办法: 添加别名select max(max_pre_nights) ,max(max_post_nights) from package_pre_post_hotel where package_id = :packageId添加别名后可以解决问题,如果SQL中的列名不是唯一的,最好都添加别名,比如 select a.id as a_id , b.id as b_i

自定义FreeBSD12镜像拉起ECS报错后的处理过程

今天帮客户做了个FreeBSD12的自定义镜像,使用自定义镜像后创建ECS后,缺发现机器启动异常,然后使用VNC去登录系统:1.将自定义的FreeBSD镜像上传到云平台的环境中后,使用自定义镜像创建虚拟机,无法正常访问,使用vnc远程机器登录,发现卡在mountroot状态:2.显示磁盘分区信息3.根据提示信息:ufs:/dev/da0s1a,然后根据实际分区情况输入ufs:/dev/vtbd0p24.修改/etc/fstab文件5.修改网卡配置文件6.编辑网卡配置文件(/etc/rc.conf

Atom远程连接服务器报错服务器版本和客户端版本不一致

Atom远程连接服务器 报错信息: Server version is different than client version Original error message: Version mismatch. Client at 139 while server at 141. 在setting里check for update 一下,然后重启编辑器!!!重启

之前的Android项目报错,新建Android项目报错,代码中找不到错误解决方案

打开一年前的东西,结果发现里面的android项目全部有个红叉,也找不到错误.新建一个项目也报错,首先确定自己的环境应该没问题,然后通过查看网上的资料,发现可能是debug的keystore到期啦. 通过网上资料的引导,做以下操作: 1.进入C:\Documents and Settings\Administrator\.android 删除路径下的debug.keystore及 ddms.cfg(我只删除了debug.kestore也可以.网上说两个都删除).(不同环境下的目录可能略有不同,可

SQL报错error:索引中丢失IN或OUT参数

简单记录下: 今天mybatis中遇到一个错误: org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [INSERT INTO law_enforce_user(user_code,name,sex,birthday) VALUES(?,?,?,?)]; SQL state [99999]; error code [170

关于mybatis中的实体类属性与数据库中的列名不一致的两种解决方法

1.我们都知道,在mybatis中写实体类的时候,是按照数据库中的列名来给实体类定义属性的,举个例子: public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; } 2.但是,如果我们在定义实体类的时候,实体类中的属性与数据库列名不一致呢?比如: