《Replication的犄角旮旯》系列导读html
Replication的犄角旮旯(一)--变动订阅端表名的应用场景算法
Replication的犄角旮旯(二)--寻找订阅端丢失的记录sql
Replication的犄角旮旯(三)--聊聊@bitmap服务器
Replication的犄角旮旯(四)--关于事务复制的监控ide
Replication的犄角旮旯(五)--关于复制identity列工具
Replication的犄角旮旯(六)-- 一个DDL引起的血案(上)(如何近似估算DDL操做进度)post
Replication的犄角旮旯(七)-- 一个DDL引起的血案(下)(聊聊logreader的延迟)url
Replication的犄角旮旯(八)-- 订阅与发布异构的问题spa
Replication的犄角旮旯(九)-- sp_setsubscriptionxactseqno,赋予订阅活力的工具代理
---------------------------------------华丽丽的分割线--------------------------------------------
关于replication中的bitmap,貌似介绍的文档很少;本文将从对此参数作一初步的简析,并介绍如何利用这个参数处理一些特定环境下的问题;
再次强调,本方法虽屡次经受验证无误,但屡次被MS supporter们建议不要尝试使用此方法,还望各位DBA三思!
先来看看@bitmap在哪里出现
咱们先建立一个表的复制订阅,表结构以下
1 USE [test_aaa] 2 GO 3 4 /****** Object: Table [dbo].[test_b] Script Date: 2014/1/23 16:12:28 ******/ 5 SET ANSI_NULLS ON 6 GO 7 8 SET QUOTED_IDENTIFIER ON 9 GO 10 11 SET ANSI_PADDING ON 12 GO 13 14 CREATE TABLE [dbo].[test_b]( 15 [id1] [int] NOT NULL, 16 [id2] [int] NOT NULL, 17 [id3] [int] NOT NULL, 18 [id4] [int] NOT NULL, 19 [name] [varchar](10) NULL, 20 [remark1] [varchar](100) NULL, 21 [remark2] [varchar](100) NULL, 22 [remark3] [varchar](100) NULL, 23 [remark4] [varchar](100) NULL, 24 CONSTRAINT [pk_id1_id2_id3_id4] PRIMARY KEY CLUSTERED 25 ( 26 [id1] ASC, 27 [id2] ASC, 28 [id3] ASC, 29 [id4] ASC 30 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 31 ) ON [PRIMARY] 32 33 GO 34 35 SET ANSI_PADDING OFF 36 GO
到订阅库的存储过程当中,找到sp_MSupd_dbotest_b,生成脚本
1 USE [test_byxl1] 2 GO 3 /****** Object: StoredProcedure [dbo].[sp_MSupd_dbotest_b] Script Date: 2014/1/23 14:28:46 ******/ 4 SET ANSI_NULLS ON 5 GO 6 SET QUOTED_IDENTIFIER ON 7 GO 8 ALTER procedure [dbo].[sp_MSupd_dbotest_b] 9 @c1 int = NULL, 10 @c2 int = NULL, 11 @c3 int = NULL, 12 @c4 int = NULL, 13 @c5 varchar(10) = NULL, 14 @c6 varchar(100) = NULL, 15 @c7 varchar(100) = NULL, 16 @c8 varchar(100) = NULL, 17 @c9 varchar(100) = NULL, 18 @pkc1 int = NULL, 19 @pkc2 int = NULL, 20 @pkc3 int = NULL, 21 @pkc4 int = NULL, 22 @bitmap binary(2) 23 as 24 begin 25 if (substring(@bitmap,1,1) & 1 = 1) or 26 (substring(@bitmap,1,1) & 2 = 2) or 27 (substring(@bitmap,1,1) & 4 = 4) or 28 (substring(@bitmap,1,1) & 8 = 8) 29 begin 30 update [dbo].[test_b] 31 set [id1] = case substring(@bitmap,1,1) & 1 when 1 then @c1 else [id1] end, 32 [id2] = case substring(@bitmap,1,1) & 2 when 2 then @c2 else [id2] end, 33 [id3] = case substring(@bitmap,1,1) & 4 when 4 then @c3 else [id3] end, 34 [id4] = case substring(@bitmap,1,1) & 8 when 8 then @c4 else [id4] end, 35 [name] = case substring(@bitmap,1,1) & 16 when 16 then @c5 else [name] end, 36 [remark1] = case substring(@bitmap,1,1) & 32 when 32 then @c6 else [remark1] end, 37 [remark2] = case substring(@bitmap,1,1) & 64 when 64 then @c7 else [remark2] end, 38 [remark3] = case substring(@bitmap,1,1) & 128 when 128 then @c8 else [remark3] end, 39 [remark4] = case substring(@bitmap,2,1) & 1 when 1 then @c9 else [remark4] end 40 where [id1] = @pkc1 and [id2] = @pkc2 and [id3] = @pkc3 and [id4] = @pkc4 41 if @@rowcount = 0 42 if @@microsoftversion>0x07320000 43 exec sp_MSreplraiserror 20598 44 end 45 else 46 begin 47 update [dbo].[test_b] 48 set [name] = case substring(@bitmap,1,1) & 16 when 16 then @c5 else [name] end, 49 [remark1] = case substring(@bitmap,1,1) & 32 when 32 then @c6 else [remark1] end, 50 [remark2] = case substring(@bitmap,1,1) & 64 when 64 then @c7 else [remark2] end, 51 [remark3] = case substring(@bitmap,1,1) & 128 when 128 then @c8 else [remark3] end, 52 [remark4] = case substring(@bitmap,2,1) & 1 when 1 then @c9 else [remark4] end 53 where [id1] = @pkc1 and [id2] = @pkc2 and [id3] = @pkc3 and [id4] = @pkc4 54 if @@rowcount = 0 55 if @@microsoftversion>0x07320000 56 exec sp_MSreplraiserror 20598 57 end 58 end
看到这么多@bitmap,是否是有种升仙的感受?
@bitmap 是binary类型,即二进制串;简单来讲,它是用来表示所操做的字段位置的参数,经过@bitmap,分发代理从distribution.dbo.msrepl_commands中读取命令时(update操做),才会知道哪些列进行了更新;
咱们先来解析一下这个存储过程;
一、根据表结构的code,咱们知道这个表共有9个字段,其中id1~id4被定义为联合主键;
因为binary(1)表示1个字节(8位的2进制),所以咱们表示9个字段的@bitmap就只能用binary(2)来容纳了;
其次,有的童鞋说,他们看到的update存储过程只有一个程序段,而个人例子中有两部分(29行~44行、46行~57行)。这个是因为存在联合主键形成的;即当被订阅的表中含有联合主键(2个或以上的字段一同做为主键)的时候才会出现两段代码,前者是更新主键列,后者则是更新非主键列;
二、根据更新列的位置不一样,@bitmap中的对应的值也不一样;
substring(@bitmap,1,1) & 1 = 1 表示第一列有更新;
substring(@bitmap,1,1) & 2 = 2 表示第二列有更新;
substring(@bitmap,1,1) & 4 = 4 表示第三列有更新;
以此类推
substring(@bitmap,1,1) & 128 = 128 表示第八列有更新;
那第九位呢? =256么? 因为1个字节只有8位,而128=2^7,当第九位出现时就要进位了
substring(@bitmap,2,1) & 1 = 1
怎么样,不难理解吧?
定义4个字段的联合主键只是为了举例说明的时候方便一些,实际的生产环境中可能不太常常能遇到;
再来看一下@bitmap在哪里能够获取到呢?我先更新一条记录,更新以前先关闭相应的分发代理(此处不须要分发命令应用到订阅端)
咱们去distribution里看看具体的分发命令(具体作法请见《Replication的犄角旮旯(二)--寻找订阅端丢失的记录》)
从命令中咱们能够看出,更新的列位置为第5(name)、7(remark2)、8(remark3)列,按照二进制的表示方法为(注意反取,即←表示第1至第9位)
0 1101 0000
因为第九位没有更新,所以为0,因此bitmap就是 1101 0000,换成十六进制就是0xD0,因为bitmap超出1个字节,所以后面再补0,就是咱们看到的0xd000了
可能看到这里,有些童鞋会说,这太麻烦了,遇到一个很宽的表,光数逗号就数死了……尽管咱们明白逗号是为了分割字段的,但系统为何会这样生成呢,这个问题能够参考:http://msdn.microsoft.com/zh-cn/library/ms152489%28v=sql.120%29.aspx
这个和article的属性,调用订阅端存储过程方式有关(SCALL),这个不是本文的重点,在此不作赘述;
根据上面的算法,咱们就能够知道,当咱们要更新一个表时,能够根据更新列的位置,推算出实际的bitmap值,但这又有什么用呢?
-------------------------------------我是华丽丽的分割线-------------------------------------
应用场景:一个表(仍是说商品表吧,比较典型),保存商品信息、简介等内容,都是varchar(max)或text类型;商家在促销活动前一般会大批量的更新这些内容(好比加个促销活动介绍等等),更新大字段是复制环境中最头疼的问题;
因为一条复制命令有长度限制(1K左右),若是一条更新记录中的更新内容过大,就会被拆分红多条命令写到msrepl_commands中(我曾经碰到过1条记录的更新操做被拆成100个复制命令),若是高峰时期有用户大量的进行这种操做,那做为DBA就能够升级为“张三疯”了;
这时候咱们就能够根据具体的update命令(通常除了更新必要的字段外,还会捎带更新updatetime这样的时间戳,具体就去找研发兄弟们要吧),计算出相应的bitmap,而后在相应的存储过程当中加个判断,屏蔽掉这样的操做;
具体操做就是,在sp_MSupd_dbotest_b这里的第2四、25行之间加上
if @bitmap=0xd000
return;
再打开分发代理,这样就屏蔽了@bitmap=0xd000的所有操做;
按照前面的操做,并无进行下面的更新
而我在此以后又从发布服务器insert了一条主键为2,2,2,2的记录,以证实该操做在update以后已经传到了订阅端,而update被屏蔽了;
须要注意的是,因为按位操做是绝对严格,对于只更新column1和同时更新column一、column2将产生不一样的bitmap,操做时必定要谨慎;