需要快速帮助。我正在使用bigint自动增量属性的数据库。我有两个需要同步的位置的数据库。由于bigint在不同站点上可能存在主键副本,因此不适合同步。我无法继续使用GUID,因为我需要更改我的代码以及数据库,这对我来说是不可能的。
现在我只有两个位置用于数据库,所以我想如果可以让我的主键自动增量总是在一个位置,在其他位置是奇数。它可以快速解决我的问题。
我如何使用计算列规范或任何其他方式来执行此操作。为了同步我正在使用Microsoft sycn框架。
如果我在同步后使用身份(1,2)服务器或身份(2,2)B服务器,则会干扰下一个增量值。例如,如果在服务器最大ID 3和B服务器当前id是4.在服务器上的同步最大ID现在将是4.我希望A服务器上的新ID应该只有5但实际上它插入6.如何能我解决了这个问题
答案 0 :(得分:3)
(补充说明:@VladimirBaranov在评论中提到了这一点,但我错过了,但这里有关于如何在这种情况下使用SEQUENCE
的充实信息)
我最初的想法是进一步回答这个问题,并且仍然可行,但我认为这个更新的选项将适合您需要的许多服务器。令我困扰的是,我知道在TSQL中有一种正确的方法可以做到这一点,我不记得它是什么。我的大脑终于在今天疏通了它:SEQUENCE。 SQL Server 2012和2014允许您定义序列以生成要在表中使用的一系列数字:
CREATE SEQUENCE oddNums
START WITH 1
INCREMENT BY 2;
GO
CREATE SEQUENCE evenNums
START WITH 0
INCREMENT BY 2;
GO
然后,不是AUTO INCREMENT
你的PK,而是从DEFAULT
给他们一个SEQUENCE
值(这些是下面链接的小提琴中的表格):
CREATE TABLE oddMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR oddNums,
[data] varchar(7)
);
CREATE TABLE evenMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR evenNums,
[data] varchar(7)
);
这些序列完全不受合并的影响,无论表中的最新PK是什么,它都将继续永远生成奇数或偶数。
Here is a SQLFiddle这在行动中。
请注意,如果执行此操作(由于DEFAULT
子句),则无法将列定义为IDENTITY,因此您必须小心插入id列,否则这应该是关于尽可能直截了当。
这可以通过您想要的任意数量的服务器来完成,只需调整每个SEQUENCE
增量的起点和起始位置,但是您可能会有一些困难(并非不可能)的时间添加额外的服务器一旦你的SEQUENCE
被定义了。
此外,here is an MSDN blog讨论了在先前版本的SQL Server上模拟SEQUENCE
的替代策略。
(注意:这是我原来的答案)今晚我一直在玩这个,取决于你如何设置东西,我想你可以放弃重新安排桌面完成同步后的每个服务器,基于表中当前的最高ID。你只需要为每个服务器做一些不同的操作,以便在一个甚至另一个上保持新的id奇怪。
所以你有:
CREATE TABLE oddMirror
(id INT NOT NULL IDENTITY(1,2),
data NVARCHAR(10))
GO
和
CREATE TABLE evenMirror
(id INT NOT NULL IDENTITY(2,2),
data NVARCHAR(10)
GO
同步两个表后,您不知道当前标识种子是奇数还是偶数,因此您需要将每个表上的重置为服务器的正确“下一个”值。所以,在oddMirror
:
DECLARE @maxId INT
DECLARE @newSeed INT
SET @maxId = (SELECT MAX(id) FROM oddMirror)
SET @newSeed = (SELECT CASE WHEN @maxId % 2 = 1 THEN @maxId ELSE @maxId -1 END)
DBCC CHECKIDENT('dbo.oddMirror', RESEED, @newSeed)
GO
evenMirror
上几乎完全相同的过程:
DECLARE @maxId INT
DECLARE @newSeed INT
SET @maxId = (SELECT MAX(id) FROM evenMirror)
SET @newSeed = (SELECT CASE WHEN @maxId % 2 = 0 THEN @maxId ELSE @maxId -1 END)
DBCC CHECKIDENT('dbo.evenMirror', RESEED, @newSeed)
GO
所以基本上,在oddMirror
,我们说,“获取当前的最大ID。如果它是奇怪的,不要改变它,但如果它是偶数,则将它备份一个。”
然后在'evenMirror'上做同样的事情,除了检查max id是否是偶数而不是奇数。
举个例子,拿这个数据:
oddMirror
1,"one"
3,"three"
5,"five"
和
evenMirror
2,"two"
4,"four"
6,"six"
8,"eight"
(注意evenMirror
有更多行)
同步后,每个表都如下所示:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
--evenMirror looks the same as this now
通过以上查询运行:
MAX(id)
上的{p> oddMirror
为8
。 8 % 2 = 0
,因此设置@newSeed = 8 - 1 = 7
,意味着oddMirror
中的下一行将获得id = 9
。
MAX(id)
上的 evenMirror
也是8
,但查询略有不同。 8 % x = 0
设置@newSeed = 8
,意味着'evenMirror will get
id = 10`中的下一行
id = 7
,但我猜这并不是一件值得关注的问题。
如果您再查询:
INSERT INTO oddMirror (data) VALUE ("data")
GO
INSERT INTO evenMirror (data) VALUE ("otherData")
GO
表格如下所示:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
9,"data"
和
evenMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
10,"otherData"
理论上可以通过更改您采用的模数来扩展以适应更多数量的服务器,并为每种可能性在WHEN
语句中添加额外的CASE
,尽管这样做肯定会很麻烦。但是,我们已经知道正确的解决方案(GUID)在这里不可用,如果您正在阅读这篇文章,那么下一个最佳解决方案(SEQUENCE
)可能无法使用,因此无论我们提出什么,不可避免地会变得很麻烦。
此方法的最大缺点是必须锁定表,直到同步过程完成。如果在完成同步并重新输入ID之前写入,则几乎肯定会发生冲突。如果这些表不是经常写的,或者你已经锁定它们进行同步,或者你的日常周期中有一个重要的“死”点(比如早上3-4点或其他什么),这可以在没有严重中断的情况下完成,这可能不是什么大不了的事,但只有你知道这是多么可行。
所以,你的设置可能会或者可能不会使这成为可能,但是我今晚在我的沙盒数据库中已经玩了很多这个,并且它似乎很好地确保在一个数据库中新的id总是奇怪的总是在另一个。
答案 1 :(得分:3)
这是一个非常简单的解决方案,但它只适用于两台服务器。它不能轻易扩展到更多服务器。
关于它的好处是它没有使用CHECKIDENT
来重新设置表格,您无需担心同时运行事务以获得准确的MAX
ID
加入CHECKIDENT
。
此外,MSDN警告identity property on a column does not guarantee the following:
服务器重启或其他故障后的连续值 - SQL Server 可能因性能原因和某些原因而缓存身份值 在数据库故障或服务器期间,分配的值可能会丢失 重新开始。这可能导致插入时身份值的缺口。如果 差距是不可接受的,然后应用程序应该使用它自己的 生成键值的机制。使用序列生成器 NOCACHE选项可以限制从不的事务的差距 提交。
如果您选择基于使用CHECKIDENT
转发身份的解决方案,则最好仔细检查它是否在这种情况下正常运行。
另外,要运行CHECKIDENT
,您可能需要特定的permissions:
调用者必须拥有该表,或者是sysadmin固定服务器的成员 角色,db_owner固定数据库角色或db_ddladmin已修复 数据库角色。
<强>解决方案强>
我的主要想法是在第一台服务器上使用IDENTITY(1,1)
,在第二台服务器上使用IDENTITY(-1,-1)
。
IDs
奇怪,甚至是积极和消极。
这是一个脚本,证明它可以按预期工作而无需任何额外的工作。
-- Sample data
CREATE TABLE #T1 (ID bigint IDENTITY(1,1), V1 int);
CREATE TABLE #T2 (ID bigint IDENTITY(-1,-1), V2 int);
INSERT INTO #T1 VALUES (11);
INSERT INTO #T1 VALUES (12);
INSERT INTO #T1 VALUES (13);
INSERT INTO #T1 VALUES (14);
INSERT INTO #T2 VALUES (21);
INSERT INTO #T2 VALUES (22);
INSERT INTO #T2 VALUES (23);
SELECT * FROM #T1;
SELECT * FROM #T2;
我们从表中的示例数据开始:
#T1
ID V1
1 11
2 12
3 13
4 14
#T2
ID V2
-1 21
-2 22
-3 23
执行同步
-- Insert into T1 new values from T2
SET IDENTITY_INSERT #T1 ON;
MERGE INTO #T1 AS Dst
USING
(
SELECT ID, V2
FROM #T2
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V1)
VALUES (Src.ID, Src.V2);
SET IDENTITY_INSERT #T1 OFF;
-- Insert into T2 new values from T1
SET IDENTITY_INSERT #T2 ON;
MERGE INTO #T2 AS Dst
USING
(
SELECT ID, V1
FROM #T1
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V2)
VALUES (Src.ID, Src.V1);
SET IDENTITY_INSERT #T2 OFF;
SELECT * FROM #T1;
SELECT * FROM #T2;
同步结果 - 两个相同的表格
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
插入更多数据以检查同步后身份如何工作
-- Insert more data into T1 and T2
INSERT INTO #T1 VALUES (15);
INSERT INTO #T1 VALUES (16);
INSERT INTO #T2 VALUES (24);
INSERT INTO #T2 VALUES (25);
INSERT INTO #T2 VALUES (26);
SELECT * FROM #T1;
SELECT * FROM #T2;
-- Clean up
DROP TABLE #T1;
DROP TABLE #T2;
同步后生成的身份
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
5 15
6 16
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
-4 24
-5 25
-6 26
您可以看到T1
中的新身份仍然是积极的,T2中的新身份仍然是负面的。
答案 2 :(得分:1)
您有两个用于插入目标表的源表:
所以我建议你这样做:
integer -or any other as you want- field and update it from 1st. source data by
1 and for the 2nd one by
2`。bigint
的{{1}}字段。现在使用此查询在null
字段中显示您的枚举ID:
nId
要在任何插入后运行此查询,您可以使用触发器。
我认为使用此解决方案,您可以获得所需的所有数据。
修改强> 一些结果:
Update <table>
Set nId = isnull((select count(ti.*) from <table> as ti where ti.pkId < <table>.pkId), 0) + 1
答案 3 :(得分:1)
一个选项,一个服务器设置为identity(1,1),另一个设置为identity(-1,-1)。身份不会重叠,将数据从一台服务器复制到另一台服务器不会影响“下一个”ID或重新播种。
当然,不适用于两台以上的服务器。
答案 4 :(得分:0)
我认为偶数/奇数方法使得这很难。此外,当您向每个节点添加行时,您将遇到页面拆分问题,尤其是当您的PK是聚簇索引时。
这些表是使用点对点复制进行复制还是手动同步?如果涉及复制,页面拆分问题将会发挥作用。
为什么不为每个节点使用数字范围?节点1的1-X和节点#2的X + 1-Y?估计行量并将范围设置得如此之大,以免发生重叠。
BIGINT的例子:
节点1:1-200000000000(2000亿行) 节点2:200000000001-600000000000(4000亿行)
离开600000000001以备将来使用。警告,身份没有最大值,你需要手动报警。
要将标识值设置为正确的数字,请使用带有RESEED选项的DBCC CHECKIDENT。如果你与偶数/奇数场景结婚,这也会奏效。
这也有一个优点,即每次插入不会分页一次,特别是如果每个节点的活动不均衡。