有两种情况会造成更新丢失,第一种是不正确的设置,例如外键或触发器的“Not For Replication” (NFR)属性没有开启。详情请参考http://blogs.msdn.com/b/apgcdsd/archive/2012/01/10/10254809.aspx

第二种是产品bug,例如使用了 MaxCmdsInTran http://support.microsoft.com/kb/2648158

前一阵我在做case的时候遇到了一个新的bug。这个bug在sql server 2005,2008,  2008R2这些版本中都可以重现,并且目前没有推出相应的fix.  需要注意的是,sql server 2012中相关的行为已经重新进行了设计,不会存在这个问题。

描述

===

环境:事务复制的订阅是通过快照进行的初始化

条件:Logreader没有运行。

操作:我们向publicaiton添加了一个新的Article.

此时在log reader停止期间到完成添加article之前,publication的article出现了更新/删除/插入,那么这些变更都不会传递到订阅。 假设 Logreader在11:00停止,我们在12:00添加了一个新的artilce(假设瞬间完成),13:00重新启动Logreader 。那么11:00~12:00之间的更新将全部丢失。

原因

===

当publicationdatabase的article发生更新时, 会产生相应的日志,Log reader会读取这些日志信息,将他们写入到Distribution 数据库的msrepl_transactions和msrepl_commands中。具体的技术细节我会在以后的文章里介绍。

Msrepl_transactions中的每一条记录都有一个唯一标识xact_seqno,xact_seqno对应日志中的LSN。 所以可以通过xact_seqno推断出他们在publicationdatabase中的生成顺序,编号大的生成时间就晚,编号小的生成时间就早。

Distributionagent包含两个进程,reader和writer。 Reader负责从Distribution 数据库中读取数据,Writer负责将reader读取的数据写入到订阅数据库.

reader是通过sp_MSget_repl_commands来读取Distribution数据库中(读取Msrepl_transactions表和Msrepl_Commands表)的数据

下面是sp_MSget_repl_commands的参数定义

CREATE PROCEDURE sys.sp_MSget_repl_commands

(

@agent_id int,

@last_xact_seqno varbinary(16),

@get_count tinyint = 0,  -- 0 = no count, 1 = cmd and tran (legacy), 2 = cmd only

@compatibility_level int = 7000000,

@subdb_version int = 0,

@read_query_size int = -1

)

这个存储过程有6个参数,在Transactionalreplication 中,只会使用前4个(并且第三个参数和第四个参数的值是固定不变的.分别为0和10000000)。下面是一个例子:

execsp_MSget_repl_commands 46,0x0010630F000002A900EA00000000,0,10000000

@agent_id表示Distributionagentid,每个订阅都会有一个单独的Distributionagent来处理数据。 带入@agent_id后,就可以找到订阅对应的publication 和所有的article。

@last_xact_seqno 表示上一次传递到订阅的LSN。

大致逻辑是:Reader读取分发数据库中LSN大于@last_xact_seqno的数据。 Writer将读取到的数据写入订阅,并更新相应的LSN.(subscription数据库的 MSreplication_subscriptions表的 transaction_timestamp列和Distribution数据库的msDistribution_history表的xact_seqno列)。然后Reader会继续用新的LSN来读取后续的数据,再传递给Writer,如此往复。

假设现在订阅段的数据已经更新到了0x0010630F000002A900EA00000000, 之后我们认为地向Msrepl_transactions表和Msrepl_Commands表插入了一批数据,xact_seqno对为0x0010630E000002A900EA00000000. 虽然这些数据的格式全部有效, 但Distributionagent是不会读取这些新加入的数据的,因为他们"太旧了"(他们的xact_seqno小于订阅的xact_senqo).

当Log reader停止时, 我们是可以添加article的。 并且相应的操作也会向msrepl_transactions和msrepl_commands插入数据,这些数据并不是由Log reader传递的,而是通过linkedserver直接向Distributor直接写入数据。 Distributionagent会读取这些数据,并更新相应的xact_seqno。 而"Log reader停止"到"添加新article"这段期间发布产生的数据的xact_seqno是小于"添加新article"的xact_seqno,所以这些跟新会丢失。 说起来比较抽象,下面举个例子。

10:00到11:00期间publication database共生成了三条事务,对应的xact_seqno分别为

0x0010630F000006B9001E

0x0010630F000006F10004

0x0010630F000006F20004

11:01将log reader停止

11:01~12:00期间publication database共生成了4条事务

0x0010630F000006F30004

0x0010630F000007080005

0x0010630F000007D40205

0x0010630F0000098C005C

但由于log reader没有启动,所以msrepl_transactions表内依然是三条数据. 12:01完成添加article的操作,msrepl_transactions内生成相应的记录0x00106310000000100100。

此时msrepl_transations内共有四条记录

此时订阅端的xact_seqno为0x0010630F000006F20004, distribution agent将读取大于0x0010630F000006F20004的数据, 只有0x00106310000000100100符合要求。 订阅段完成的所有的数据同后,相应的xact_seqno为0x00106310000000100100

此时开启log reader, 累计的数据传递完毕后,msrepl_transactions共有8条记录,但distribution agent不会再去读取之前的数据了…

影响

===

在有些极端情况下,即使您认为Log reader处于运行状态,还是会出现跟新丢失的情况。原因在有些情况下,Log reader成为了deadlock的牺牲者被kill掉,但Log readeragentjob有自动retry的机制,会在一段时间后自动恢复, 所以您可能无法察觉。 是否出现死锁,您查询MSLog reader_history,MSrepl_errors会找到类似的"N'Transaction (Process ID 400) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.',NULL,1,N'  SQL Log Reader Agent encountered an error.    Publisher: xxx Publisher Database: xxx".

解决办法

===

将Distributor升级至sql servr 2012

如果您暂时没有办法升级,可以采用以下两种方法:

  1. 添加一个新的发布,将新的article添加到发布中。
  2. 在添加article前将Log reader和Distributionagent停止。添加完成后,启动Log reader,确认Log reader已经将之前的数据传送到了Distribution数据库后(可以使用tracertoken来确认Log reader是否已经完成同步), 启动Distributionagent。这样就可以避免数据丢失了。

更多(2013.10.23补充)

===

如果添加一个非快照初始化的订阅时,该发布对应的发布数据库的所有已存在订阅也会出现更新丢失的情况,无论这些都订阅通过何种方式初始化,或属于那个发布.

因为添加订阅的操作也会通过linked server向distribution database内写入数据

解决方法和之前相同

该问题已经在sql server 2008 r2 sp2 cu13中解决(2014.07.01补充)

http://support.microsoft.com/kb/2922526

一个事务复制的bug--更新丢失的更多相关文章

  1. 一个事务复制的bug--更新丢失 续

    阅读本文之前请参考http://www.cnblogs.com/stswordman/p/3258897.html 最近又做了一个case,环境是sql server 2008 R2. 客户添加了一个 ...

  2. SqlServer 使用脚本创建分发服务及事务复制的可更新订阅

    原文:SqlServer 使用脚本创建分发服务及事务复制的可更新订阅 [创建使用本地分发服务器] /************************[使用本地分发服务器配置发布]*********** ...

  3. sqlserver 2000事务复制问题

    2000现在用的估计不多了,把之前收集的一些复制问题整理发布出来.可能都是些很白很二的问题,但人总是由最初的无知不断成长,不对之处欢迎指正. sqlserver 2000事务复制问题服务器A(发布) ...

  4. SQL Server 2000事务复制问题

    2000现在用的估计不多了,把之前收集的一些复制问题整理发布出来.可能都是些很白很二的问题,但人总是由最初的无知不断成长●-● SQL Server 2000事务复制问题服务器A(发布) 服务器B(分 ...

  5. sqlserver 2005 分布式架构 对等事务复制 .

    http://www.cnblogs.com/qanholas/archive/2012/03/22/2412444.html     一.为什么要使用对等事务复制 首先要说明的是使用sqlserve ...

  6. 【数据库-Azure SQL Database】如何创建事务复制将本地数据同步到 SQL Azure

    Azure SQL DB 可以被配置成为 SQL Server 事务复制的一个订阅者( subscriber ). 主要应用场景有两种: 将您的数据迁移到 Azure SQL DB, 并且没有宕机时间 ...

  7. Mysql锁机制--并发事务带来的更新丢失问题

    Mysql 系列文章主页 =============== 刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题.可 ...

  8. 一个简易的四则运算单元...(15.12.15 BUG更新)

    网上找的, 没有作者信息, 只能在这里感谢一下了, 支持标准写法的四则运算 --2015-12-15 修改了一个内存泄漏的BUG - Pop方法没有释放申请的内存 unit Base.Calculat ...

  9. 事务复制5: Transaction and Command

    事务复制使用 dbo.msrepl_transactions 和 dbo.MSrepl_commands 存储用于数据同步的Transaction和Command.在replication中,每个co ...

随机推荐

  1. spring-data-redis注册fastjson序列化工具

    使用spring-data-redis的时候,其序列化工具自带:

  2. iis7下配置php出现404.17错误的解决办法

    服务器上建有几个PHP站点,都在正常运行.今天又新建了一个PHP站点,处理程序模块配置的和其他几个都一样,但就是跑不起来,一直提示404.17错误,重启服务器也不行. 最后实在没办法了,就把正常运行站 ...

  3. android图片处理方法

    Java代码 //压缩图片大小 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ...

  4. Ant: Class not found: javac1.8

    今天用ant,在选择build.xml,run as ant build后出错Ant: Class not found: javac1.8 分析问题:是否是eclipse中的ant版本和java的版本 ...

  5. 火狐通行证升级为Firefox Sync后,如何在多设备间同步书签等信息

    一直在使用Firefox的一个比较重要的原因是习惯了它的书签同步功能,之前一直是使用火狐通行证来实现多设备间同步的,最近新装了WIN8.1系统来学习,结果装上新版Firefox之后,发现无论怎么弄也没 ...

  6. 以太坊只能合约摸索——第一关,ubuntu开发环境部署

    1. 安装“eth”命令行工具 sudo add-apt-repository ppa:ethereum/ethereum-qt sudo add-apt-repository ppa:ethereu ...

  7. Linux常用命令笔记一

    笔记1:查看ubuntu是32位还是64位的方法及其版本号 查看ubuntu是64还是32的命令如下: uname -m 如果是i386到i686,那就是32位系统:如果是x86_64 ,那就是64系 ...

  8. php的cookie和session相同主域名共享

    如何使用chrome查看cookie和session详见另一篇文章,点这里 首先说cookie, $cookieDomain = '.elf.com'; setcookie('elf', 'im el ...

  9. BLE资料应用笔记 -- 持续更新

    BLE资料应用笔记 -- 持续更新 BLE 应用笔记 小书匠 简而言之,蓝牙无处不在,易于使用,低耗能和低使用成本.'让我们'更深入地探索这些方面吧. 蓝牙无处不在-,您可以在几乎每一台电话.笔记本电 ...

  10. wifi,网关相关标识的获取

    获取WIFI的相关信息 - (void)getWifiInfo { NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfa ...