• 在发布订阅中增加rowversion数据类型字段引发错误(or bug)


    考虑这样的场景,现在每天要同步更新的数据,为了记录哪些数据发生过变更,需要对表添加一个rowversion列来标识被更新过。恰好这个表比较大,而且有发布订阅。由于该字段对于订阅表来讲,无实际意义,而且添加一个有默认值的不可为空的8字节字段,会产生数据空间分配,或许带来大量的页拆分,对IO压力会加大,所以考虑将此表的发布项暂时改为不支持架构复制,等添加完该字段后,再将其改为支持架构复制。

    下面来做这个测试:

    1.下面是一个正常的事务发布,将DB_1中的T_1发布到DB_2的T_1表中。

    2.修改发布项DBrepTEST关于架构复制的选项,设置为不支持

    DECLARE @publication AS sysname
    SET @publication = N'DBrepTEST'

    USE DB_1
    EXEC sp_changepublication
    @publication = @publication,
    @property = N'replicate_ddl',
    @value = 0
    GO

    3.然后,添加一个字段,类型为rowversion

    use DB_1
    alter table t_1 add rv rowversion

    4.经过观察,发布订阅一切正常,该字段并未被创建在订阅库DB_2上


    5.我们在另外一个进程开始一个sql,向表DB_1.dbo.T_1 insert data.

    while 1=1
    begin
    waitfor delay '00:00:01'
    insert into DB_1.dbo.T_1(name)select 'a'
    end

    6.发现数据已同步到DB_2.dbo.T_1,但rv字段并未被同步,现在一切正常

    7.现在将发布项DBrepTEST修改回支持架构复制

    DECLARE @publication AS sysname
    SET @publication = N'DBrepTEST'

    USE DB_1
    EXEC sp_changepublication
    @publication = @publication,
    @property = N'replicate_ddl',
    @value = 1
    GO

    8.然后,此时向表DB_1.dbo.T_1新加一个int类型字段

    use DB_1
    go
    alter table t_1 add age int

    9.此时发布订阅出错


    出错文字为:
    Command attempted:
    if @@trancount > 0 rollback tran
    (Transaction sequence number: 0x000000580000013C000100000000, Command ID: 6)

    Error messages:
    A DDL change has been replicated. (Source: MSSQL_REPL, Error number: MSSQL_REPL27332)
    Get help: http://help/MSSQL_REPL27332
    列名 'rv' 无效。 (Source: MSSQLServer, Error number: 207)
    Get help: http://help/207
    10.由上面的信息,可以看出此时是由于在订阅库上找不到rv这个列造成的,为了确定这一点,我们看下这个事务执行的是什么

    use distribution
    go
    EXEC sp_browsereplcmds '0x000000580000013C000100000000','0x000000580000013C000100000000'

    11.由10的结果中的command列,得到如下需要应用到订阅库的t-sql

    ALTER TABLE [dbo].[T_1] add age int
    if object_id(N'[dbo].[sp_MSins_dboT_1]', 'P') > 0 drop proc [dbo].[sp_MSins_dboT_1]
    --
    if object_id(N'dbo.MSreplication_objects') is not null delete from dbo.MSreplication_objects where object_name = N'sp_MSins_dboT_1'
    --
    create procedure [dbo].[sp_MSins_dboT_1]
    @c1 int,
    @c2 varchar(20),
    @c3 int
    as
    begin
    insert into [dbo].[T_1](
    [id],
    [name],
    [rv],
    [age]
    ) values (
    @c1,
    @c2,
    default,
    @c3 )
    end
    --
    if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null exec sp_executesql @statement = N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type) values (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')', @parameters = N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname', @object_n
    ame = N'sp_MSins_dboT_1', @publisher = N'mySrv\SQL2008', @publisher_db = N'DB_1', @publication = N'DBrepTEST', @article = N'T_1'
    --
    --
    if object_id(N'[dbo].[sp_MSdel_dboT_1]', 'P') > 0 drop proc [dbo].[sp_MSdel_dboT_1]
    --
    if object_id(N'dbo.MSreplication_objects') is not null delete from dbo.MSreplication_objects where object_name = N'sp_MSdel_dboT_1'
    --
    create procedure [dbo].[sp_MSdel_dboT_1]
    @pkc1 int as begin
    delete [dbo].[T_1] where [id] = @pkc1
    if @@rowcount = 0 if @@microsoftversion>0x07320000 exec sp_MSreplraiserror 20598
    end
    --
    if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null exec sp_executesql @statement = N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type) values (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')', @parameters = N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname', @object_n
    ame = N'sp_MSdel_dboT_1', @publisher = N'mySrv\SQL2008', @publisher_db = N'DB_1', @publication = N'DBrepTEST', @article = N'T_1'
    --
    --
    if object_id(N'[dbo].[sp_MSupd_dboT_1]', 'P') > 0 drop proc [dbo].[sp_MSupd_dboT_1]
    --
    if object_id(N'dbo.MSreplication_objects') is not null delete from dbo.MSreplication_objects where object_name = N'sp_MSupd_dboT_1'
    --
    create procedure [dbo].[sp_MSupd_dboT_1]
    @c1 int = NULL,
    @c2 varchar(20) = NULL,
    @c3 int = NULL,
    @pkc1 int = NULL,
    @bitmap binary(1) as begin
    update [dbo].[T_1] set
    [name] = case substring(@bitmap,1,1) & 2 when 2 then @c2 else [name] end,
    [age] = case substring(@bitmap,1,1) & 4 when 4 then @c3 else [age] end where [id] = @pkc1
    if @@rowcount = 0 if @@microsoftversion>0x07320000 exec sp_MSreplraiserror 20598
    end
    --
    if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null exec sp_executesql @statement = N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type) values (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')', @parameters = N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname', @object_n
    ame = N'sp_MSupd_dboT_1', @publisher = N'mySrv\SQL2008', @publisher_db = N'DB_1', @publication = N'DBrepTEST', @article = N'T_1'
    --
    --

    可以看到对于添加字段,会重新创建对表操作的三个proc,本例中,insert这个proc会将rv加入进来,并且值为default,如下所示,但由于该列并不存在于订阅中,所以就会报错了,由此造成分发中断,后续事务无法应用到订阅端。

    create procedure [dbo].[sp_MSins_dboT_1] 
    @c1 int,
    @c2 varchar(20),
    @c3 int
    as
    begin
    insert into [dbo].[T_1](
    [id],
    [name],
    [rv],
    [age]
    ) values (
    @c1,
    @c2,
    default,
    @c3 )

    12.虽然可以通过在订阅端手工端建一个rv列,将其设置为可为null的varbinary(8)类型,但这个列对于订阅并没有实际意义。

    use DB_2
    go
    alter table t_1 add rv varbinary(8)


    总结,经过测试,发现当添加其它类型字段,并设置为有默认值且not null时,不会有此错误出现,目前发现仅是rowversion类型列,不知是否为SQL Server bug.
    测试环境:windows server 2003 enterprise 32bit sql server 2008 sp2 standard 32bit







    作者:nzperfect
    出处:http://www.cnblogs.com/nzperfect/
    引用或者转载本BLOG的文章请注明原作者和出处,并保留原文章中的版权信息。

  • 相关阅读:
    linq to entity group by 时间
    Entity Framework Core for Console
    EF Core 多个DbContext迁移命令
    .net for TCP服务端 && 客户端
    创建Windows Service
    EF Code First 快速创建
    在Docker中创建Mongo容器的后续设置
    Docker入门
    Python之collections.defaultdict
    Hough transform(霍夫变换)
  • 原文地址:https://www.cnblogs.com/nzperfect/p/2270427.html
Copyright © 2020-2023  润新知