在与服务器同步时处理客户端数据库中的主键冲突

本文关键字:冲突 数据库 客户端 服务器 同步 处理 | 更新日期: 2023-09-27 17:50:42

问题

如何避免客户端/服务器数据库中的主键冲突

背景

我正在同步多个数据库。我有一个中央SQL Server数据库和许多客户端SQL Server数据库。现在假设我在数据库中有一个表X,它有一个主键ID。

在第一个客户端中,我们在表X中有以下ID:

X(客户端1(
--------
1|SomeValue
2|SomeValue
3|SomeValue
4|SomeValue

在第二个客户端中,我有

X(客户端2(
--------
1|SomeValue
2|SomeValue
3|SomeValue
4|SomeValue

当我同步时,我希望客户端只上传他们的数据,而不是下载它们。

现在,当我将第一个客户端与服务器同步时,它将添加(1..4(中的主键。但是,当我与第二个客户端同步时,将出现PRIMARY KEY CLASH。我该如何解决这个问题?

我使用的是SQL Server 2008 R2、同步框架和C#。

我已经考虑过使用GUID作为主键的想法,这在我的情况下是不可行的,因为我处理的是遗留数据库。此外,对IDENTITY值进行补种的想法有点容易出错。如何在不插入值的情况下递增标识列?P.S:主键设置为IDENTITY,增量为1。

在与服务器同步时处理客户端数据库中的主键冲突

为了支持能够插入记录的分布式客户端,架构必须支持客户端DB在没有冲突的情况下创建行。这意味着对PK使用GUID,或者使用连接键。(ID+ClientID(*无论哪种方式,模式更改。

手动同步客户端数据库意味着在插入或处理异常之前检查冲突的ID,然后替换ID或允许为冲突的记录生成标识。这意味着更新所有FK关系。耗时且容易出错。

有几种方法可以实现这一点。第一个问题是,是否有一个保证的uniquekey,上游和下游数据库都可以通过它引用该记录。在SQL server的情况下,您需要的密钥类型是UNIQUEIDENTIFIER(又名GUID(。

现在,如果由于某种原因不能使用UNIQUEIDENTIFIER,第二个最好的选择是添加一个额外的列作为位置或类似的列。此列将标识记录来自的单个数据库。例如,第一个客户端的位置ID可能为1,第二个为2,等等。然后,您可以将现有的主键修改为该位置ID和当前ID的复合密钥。

第三种选择是根本不修改客户端数据库,而只将位置id列添加到"master"数据库中的表中。如果您在代码中手动同步,那么代码需要考虑记录的来源,并根据需要添加id。这可能很困难,但肯定是可能的。

假设您必须为客户端数据库重新设定种子,以便在特定点启动它们的标识值。例如,客户端1可能获得值1到10000,客户端2可能获得值10001到20000。然而,你可以在这里看到失败的可能性。不仅是如果客户端超过了您允许的范围,而且如果您设置了其他客户端,并且有人破坏了种子值。此外,这将是一个全面的PITA来修复所有现有的客户。


顺便说一句,当构建一个应用程序时,您知道它将拥有一个需要合并的分布式数据库,那么简单地使用GUID作为主键几乎是普遍明智的。是的,它会对索引等进行分段,但在这种情况下,这些缺点比完整的PITA更可取,而且当你最终将所有数据合并在一起时可能会出现错误。

进一步到@Chris的第三个选项,如果你使用同步框架,你实际上可以"欺骗"它为服务器端PK添加一个额外的列。

请参阅:第1部分-上传同步,其中客户端和服务器主键不同