1

我有一个应用程序设置,事务复制被推送到备用机器,用于紧急故障转移。复制似乎正在工作,对服务器 1 所做的任何插入都将自动出现在服务器 2 上。

但是,我不能完全让故障转移工作。在服务器 1 变得不可用的情况下(这是唯一会使用服务器 2 的情况,因此复制是单向的),这个想法是工作应该在服务器 2 上继续,并且过渡应该是无缝的因为所有数据都已被复制。

但是当移动到服务器 2 时,在确保服务器 1 上的所有更新都已传输后,对于某些表,我不断收到主键违规异常。

违反主键约束“PK_TableA”。无法在对象“dbo.TableA”中插入重复键。

一个简单的查询,例如

INSERT INTO TableA (Field1, Field2, TableB_ID) VALUES ('a','b', 6)

将产生上述错误。似乎当我指示表分配它自己的标识时,通过从查询中省略它(TableA有一个ID int identity(1,1)字段),SQL Server 将自动分配一个违反 PK 约束的 ID。为什么会这样?


TableA有一个触发器INSERTDELETE它做一个简单的非规范化工作

UPDATE TableB
SET Count = (SELECT COUNT(1) FROM TableA WHERE TableB.ID = TableA.TableB_ID)
WHERE ID IN(
    -- Fetch affected ID's from deleted or inserted rows
    SELECT DISTINCT TableB_ID FROM deleted
    UNION
    SELECT DISTINCT TableB_ID FROM inserted
)

在复制时,这意外地不是 Server 2 数据库的一部分,后来我插入了它。现场的一致性TableB.Count对于手头的任务并不重要。PK 冲突发生在服务器 2 中存在触发器之前以及创建它之后。


At both Publisher and Subscriber, the ID field that is yielding the violations has the following definition:

[ID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL

I suppose the NOT FOR REPLICATION part is redundant on the Publisher, as no replication job will ever write to it, but I can't see that it should be the cause of the problem, either.

4

2 回答 2

1

IDENTITY ranges must be managed explicitly in a replication scenario.

In your case you need to find, for each IDENTITY, a value larger than all current values.

Then you can configure one server to assign only odd numbers and the other only even numbers. JUst change the definition to IDENTITY(MAXPLUS1,2) on the publisher and IDENTITY(MAXPLUS2,2) on the subscriber.

Obviously you can extend this scheme to support any number of susbcribers.

于 2012-05-24T10:09:13.050 回答
0

I ran into the same problem - the CheckIdent method is also what saved me, here's a quick and dirty script that got me going.

If you send the output of this query to text (not grid results, it's a button in the SSMS toolbar) you can copy/paste it into a new query window and get another set of output that has all the checkIdent commands for every table in the database with the right seed value (assuming you always increment by one on every table that has an identity). You can then take those results (if you see some nulls in the second query window you may need to omit those manually) and copy/paste into a third query window and execute it.

Not a glamorous solution, but you can get a DB back up and running really quickly if you're in a bind (your main DB server went away and you need a replica (maybe the only replica?) to become the primary server.

SELECT 'select ''[' + s.name + '].[' + t.name + ']'' as TableName, ''DBCC CHECKIDENT(''''[' + s.name + '].[' + t.name + ']'''', ''''RESEED'''','' + cast(max ([' + s.name + '].[' + t.name + '].[' + i.name + '])' + '+1 AS varchar(50)) + '')'' as reseed from [' + s.name + '].[' + t.name + ']'
+ char(13) + char (10) + 'union all' 
FROM sys.schemas AS s
INNER JOIN sys.tables AS t
ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.identity_columns i
on i.[object_id] = t.[object_id]
order by t.name
于 2018-12-16T18:22:16.553 回答