AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 20132
Accepted
peter
peter
Asked: 2012-06-29 20:24:29 +0800 CST2012-06-29 20:24:29 +0800 CST 2012-06-29 20:24:29 +0800 CST

解决合并复制中的过滤问题

  • 772

我正在为我们的数据库的合并复制发布定义过滤器。

我遇到的问题是合并复制有这些规则,我不喜欢称它们为限制,因为我能理解其目的。

1) 创建文章过滤器时,您不能包含子查询,或者至少不应该包含子查询。如果你这样做了,那么它会在你第一次同步时工作,但如果子查询表中的某些内容发生变化,则不会重新评估过滤器。

2)使用连接过滤器时,您只能将两个表连接在一起。

我遇到的问题是我们数据库中的关系比这更复杂。例如,这是我们的关系之一,

user <- regions selected <- STREET LIGHTS -> settings (are we syncing street lights) -> user

这是一个示例表结构,可以更详细地解释上述内容,

用户表,

Id    Username
1     petermc

UserRegion Table,(将其视为安全表,允许特定用户查看哪个区域)

Id    UserId    RegionId
1     1         2
1     2         4

同步设置表,

Id    UserId    Table
1     1         StreetLight

区域表(这只是一个显示一些示例区域的查找表)

Id    Region Name
1     North Auckland
2     South Auckland
3     Central Auckland
4     Great Barrier Island

路表,

Id    RegionId    Name
1     1           Rosedale Rd
2     1           North Shore Rd

路灯表

Id    RoadId    Last Replaced
1     1         2012-05-01
2     1         2009-06-03
3     2         2001-06-08

所以在这种情况下,如果我写了一个选择语句来应用我对petermc的过滤,它看起来像这样,

select * from StreetLight where
roadId in (select Id from Road where RegionId in (select regionId from UserRegion where userid = 1))
and exists (select 1 from syncsetting where userid = 1 and [table] = 'StreetLight')

所以我在那里做两件事。首先基于区域进行过滤,以将非常大的表减少为更小更易于管理的子集。

第二个是指定订阅者是否对 StreetLight 表感兴趣。如果不是,那么订阅者应该有一个空的 StreetLight 表。这部分很重要,因为发布中将包含大量表,因此包含订阅者不会使用的内容是没有意义的。

我们最大的数据库在某些表中有数百万条记录,这些记录也会有适度的更新。我们必须让这个过滤正确。不过滤这些表的选项是不可行的。

sql-server merge-replication
  • 2 2 个回答
  • 2196 Views

2 个回答

  • Voted
  1. RBarryYoung
    2012-07-05T08:48:36+08:002012-07-05T08:48:36+08:00

    这是一个复杂的情况,但我认为最好的解决方案是创建一个附加表(我将其称为 UserFilters),将来自 Road、UserRegion 和 SyncSetting 表的合并数据保存在单个组合行格式中,并且然后将您的复制过滤器加入该表。此 UserFilters 表需要由组成表上的触发器维护,以便当 Road、UserRegion 或 SyncSetting 中的行发生更改时,UserFilters 中的行将自动更改以匹配。我将在下面演示如何执行所有这些操作。

    首先是注释;此表 (UserFilters) 是一个非规范化表,它是您正常应用程序数据模式的附属表。也就是说,它是一个操作工件,仅用于操作复制技术以执行您想要的操作,而不是应用程序数据设计/架构的一部分。这种非规范化的使用被认为是可以接受的,因为它只是为了解决技术限制,而不是由您的应用程序直接使用。

    好的,这是如何做到的:

    1. 以 JOIN 形式编写您想要的过滤器查询:

    这是您的原始过滤器查询(展开,以便更容易分解):

    select  * 
    from    StreetLight 
    where   roadId in 
        (
        select  Id 
        from    Road 
        where   RegionId in 
            (
            select  regionId 
            from    UserRegion 
            where   userid = 1
            )
        )
      and   exists 
        (
        select  1 
        from    syncsetting 
        where   userid  = 1 
          and   [table] = 'StreetLight'
        )
    

    我们想将其重写为只有 JOIN 和 WHERE,但没有子查询的格式。(这总是可以做到的,但我不会在本文中讨论如何做到这一点。)就像这样:

    SELECT  StreetLight.* 
    FROM    StreetLight 
    JOIN    Road        ON  Road.Id             = Streetlight.roadId
    JOIN    UserRegion  ON  UserRegion.regionid = Road.RegionId
    JOIN    syncsetting ON  syncsetting.userid  = UserRegion.userid
                        And syncsetting.[table] = 'StreetLight'
    Where   UserRegion.userid = 1
    

    2. 创建一个合并所有过滤条件的视图

    我们使用上面的 JOIN 子句创建一个视图,该视图仅包含与复制过滤器相关的列,并投射所有必要的行以完全描述所有过滤器条件。这比听起来容易,它应该是这样的:

    CREATE VIEW vwUserFilters As
    SELECT
            Road.Id             As RoadId,
            Road.RegionId       As RegionId,
            UserRegion.userid   As UserId,
            syncsetting.[table] As syncTable
    FROM    Road
    JOIN    UserRegion  ON  UserRegion.regionid = Road.RegionId
    JOIN    syncsetting ON  syncsetting.userid  = UserRegion.userid
    --  Comment the next line to generalize for all tables
    --                  And syncsetting.[table] = 'StreetLight'
    

    请注意,我们还泛化了 View 的表达式以涵盖所有用户。此外,我们已经注释掉了 SyncSetting "[table] = 'StreetLight'",以便为 SyncSetting 和 Road 所涵盖的所有表格概括这一点(这可能有效也可能无效,您必须决定这一点)。

    3. 创建 UserFilters 表

    使用视图作为列的指南创建 UserFilters 表。但是,出于键/索引性能的原因,我们希望添加一个 IDENTITY 列:

    CREATE TABLE adjUserFilters 
    (
        filterId    INT Identity(1,1) PRIMARY KEY,
        RoadId      INT         NOT NULL,
        RegionID    INT         NOT NULL,
        userid      INT         NOT NULL,
        syncTable   VARCHAR(32) NOT NULL
    )
    

    然后使用视图填充它:

    INSERT INTO adjUserFilters 
    SELECT RoadId, RegionID, UserId, syncTable FROM vwUserFilters
    

    出于性能和锁定原因,您需要很多索引路径。我不能确定它们应该是什么,但这是我要开始的:

    CREATE UNIQUE INDEX IX_adjUserFilters 
        ON dbo.adjUserFilters(userid, syncTable, RoadId)
    CREATE INDEX IX_adjUserFilters_1 
        ON dbo.adjUserFilters(RegionID, userid)
    CREATE NONCLUSTERED INDEX IX_adjUserFilters_2 
        ON dbo.adjUserFilters(RoadId, userid)
    

    4. 向成分表添加维护触发器

    您需要向 Road、UserRegion 和 SyncSetting 表添加触发器,以在修改这些表时使 [adjUserFilters] 的内容与这些表保持同步。它们应该如下所示: 首先是 Road 表:

    CREATE TRIGGER dbo.trRoad_MaintainUserFilters_IUD 
       ON  dbo.Road
       AFTER INSERT,DELETE,UPDATE
    AS 
    BEGIN
        SET NOCOUNT ON;
    
        -- First, Remove any filter rows corresponding to any DELETE or UPDATE rows
        DELETE FROM adjUserFilters
        WHERE   RoadId IN(Select d.Id From deleted As d)
    
        -- Now, Add in any rows implied by any INSERT or UPDATE rows
        INSERT INTO adjUserFilters
        SELECT  v.RoadId, v.RegionID, v.UserId, v.syncTable 
        FROM    vwUserFilters As v
        JOIN    inserted      As i  ON i.Id = v.RoadId
    END
    

    用户区域表:

    CREATE TRIGGER dbo.trUserRegion_MaintainUserFilters_IUD 
       ON  dbo.UserRegion
       AFTER INSERT,DELETE,UPDATE
    AS 
    BEGIN
        SET NOCOUNT ON;
    
        -- First, Remove any filter rows corresponding to any DELETE or UPDATE rows
        DELETE FROM adjUserFilters
        WHERE   EXISTS(
            Select * From deleted As d 
            Where   d.UserId    = adjUserFilters.userid
              And   d.RegionId  = adjUserFilters.RegionId
            )
    
        -- Now, Add in any rows implied by any INSERT or UPDATE rows
        INSERT INTO adjUserFilters
        SELECT  v.RoadId, v.RegionID, v.UserId, v.syncTable 
        FROM    vwUserFilters As v
        JOIN    inserted      As i  
            ON  i.UserId    = v.userid
            And i.RegionId  = v.RegionId
    END
    

    最后是 SyncSetting 表:

    CREATE TRIGGER dbo.trSyncsetting_MaintainUserFilters_IUD 
       ON  dbo.syncsetting
       AFTER INSERT,DELETE,UPDATE
    AS 
    BEGIN
        SET NOCOUNT ON;
    
        -- First, Remove any filter rows corresponding to any DELETE or UPDATE rows
        DELETE FROM adjUserFilters
        WHERE   EXISTS(
            Select * From deleted As d 
            Where   d.UserId    = adjUserFilters.userid
              And   d.[table]   = adjUserFilters.syncTable
            )
    
        -- Now, Add in any rows implied by any INSERT or UPDATE rows
        INSERT INTO adjUserFilters
        SELECT  v.RoadId, v.RegionID, v.UserId, v.syncTable 
        FROM    vwUserFilters As v
        JOIN    inserted      As i  
            ON  i.UserId    = v.userid
            And i.[table]   = v.syncTable
    END
    

    5. 创建您的复制过滤器查询

    现在您可以为“user1”创建复制查询过滤器,如下所示:

    select  StreetLight.* 
    from    StreetLight     As t
    JOIN    adjUserFilters  As f  
        ON  f.syncTable = 'StreetLight'
        And f.RoadId    = t.RoadId
    Where   f.userid    = 1 
    -- (corrected)
    

    根据要求,这是您将用于定期同步 adjUserFilters 的脚本,而不是来自触发器:

    DELETE FROM adjUserFilters;
    
    INSERT INTO adjUserFilters 
    SELECT RoadId, RegionID, UserId, syncTable FROM vwUserFilters
    

    顺便说一句,我不确定您将如何确保它在合并同步作业之前或作为合并同步作业的一部分运行,但它可能可以完成。

    • 4
  2. Best Answer
    peter
    2012-12-18T12:34:22+08:002012-12-18T12:34:22+08:00

    我必须发出警告。我们现在已经远离了合并复制,我不得不建议上面的方案可能是一个主要的性能问题。

    例外情况要求您拥有一个小型发布,其中包含少量过滤和/或不是多层深度的过滤器。例如 10 - 100 篇文章可能工作正常。如果您过多地推动上述方案,您可能会遇到性能/锁定问题。

    每次将记录插入顶部过滤表时,合并复制触发器都必须处理所有子表。它还将记录添加到所有子表的 MSMerge_contents 和 MSMerge_genhistory。您拥有的子表越多,如果它们中有大量记录,则需要更多的处理能力。

    我们遇到了 sp_MSsetupbelongs 太慢和超时的问题。最后我们得出的结论是,我们过度推动了合并复制,并且这项技术对我们不起作用。

    这导致我建议,如果开箱即用的合并复制中的过滤方案对于您的情况不够灵活,那么要么不要过滤,要么不要使用合并复制。测试测试测试当然,每种情况都不同。

    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何查看 Oracle 中的数据库列表?

    • 8 个回答
  • Marko Smith

    mysql innodb_buffer_pool_size 应该有多大?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    从 .frm 和 .ibd 文件恢复表?

    • 10 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve