zoukankan      html  css  js  c++  java
  • Replication的犄角旮旯(三)聊聊@bitmap

    《Replication的犄角旮旯》系列导读

    Replication的犄角旮旯(一)--变更订阅端表名的应用场景

    Replication的犄角旮旯(二)--寻找订阅端丢失的记录

    Replication的犄角旮旯(三)--聊聊@bitmap

    Replication的犄角旮旯(四)--关于事务复制的监控

    Replication的犄角旮旯(五)--关于复制identity列

    Replication的犄角旮旯(六)-- 一个DDL引发的血案(上)(如何近似估算DDL操作进度)

    Replication的犄角旮旯(七)-- 一个DDL引发的血案(下)(聊聊logreader的延迟)

    Replication的犄角旮旯(八)-- 订阅与发布异构的问题

    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
    View Code

      到订阅库的存储过程中,找到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   
    View Code

      看到这么多@bitmap,是不是有种升仙的感觉?

      @bitmap 是binary类型,即二进制串;简单来说,它是用来表示所操作的字段位置的参数,通过@bitmap,分发代理从distribution.dbo.msrepl_commands中读取命令时(update操作),才会知道哪些列进行了更新;

      我们先来解析一下这个存储过程;

    1、根据表结构的code,我们知道这个表共有9个字段,其中id1~id4被定义为联合主键;

         由于binary(1)表示1个字节(8位的2进制),因此我们表示9个字段的@bitmap就只能用binary(2)来容纳了;

      其次,有的童鞋说,他们看到的update存储过程只有一个程序段,而我的例子中有两部分(29行~44行、46行~57行)。这个是由于存在联合主键造成的;即当被订阅的表中含有联合主键(2个或以上的字段一同作为主键)的时候才会出现两段代码,前者是更新主键列,后者则是更新非主键列;

    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这里的第24、25行之间加上

    if @bitmap=0xd000

    return;

    再打开分发代理,这样就屏蔽了@bitmap=0xd000的全部操作;

    按照前面的操作,并没有进行下面的更新

    而我在此之后又从发布服务器insert了一条主键为2,2,2,2的记录,以证明该操作在update之后已经传到了订阅端,而update被屏蔽了;

    需要注意的是,由于按位操作是绝对严格,对于只更新column1和同时更新column1、column2将产生不同的bitmap,操作时一定要谨慎;

  • 相关阅读:
    Angular Universal 学习笔记
    SAP Spartacus 如何获得当前渲染页面的 CMS 元数据
    Angular 服务器端渲染的学习笔记(二)
    Angular 服务器端渲染的学习笔记(一)
    第三方外部 Saas提供商如何跟使用 SAP 系统的客户进行对接接口集成
    如何从 SAP Spartacus Product Detail 页面,找到其 Angular 实现 Component 的位置
    具备自动刷新功能的 SAP ABAP ALV 报表
    C++学习目录
    c--条件编译
    c--文件读写--二进制
  • 原文地址:https://www.cnblogs.com/diabloxl/p/3531601.html
Copyright © 2011-2022 走看看