Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 高频插入时使用复合主键的重复密钥冲突_Sql_Sql Server_Sql Server 2014 - Fatal编程技术网

Sql 高频插入时使用复合主键的重复密钥冲突

Sql 高频插入时使用复合主键的重复密钥冲突,sql,sql-server,sql-server-2014,Sql,Sql Server,Sql Server 2014,我的表格如下: CREATE TABLE [Alg].[Sequence]( [Id] [int] IDENTITY(1,1) NOT NULL, [SequenceId] [int] NOT NULL, [CustomerId] [bigint] NOT NULL, [Data] [text] NULL, CONSTRAINT [PK_Sequence] PRIMARY KEY CLUSTERED ( [SequenceId] ASC, [C

我的表格如下:

CREATE TABLE [Alg].[Sequence](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [SequenceId] [int] NOT NULL,
    [CustomerId] [bigint] NOT NULL,
    [Data] [text] NULL,
 CONSTRAINT [PK_Sequence] PRIMARY KEY CLUSTERED 
(
    [SequenceId] ASC,
    [CustomerId] ASC
)
这是另一个表的insert/update触发器的一个块,在这里我将数据插入到
序列
表:

--insert data into sequence table
SELECT @MaxSeqId =  ISNULL(MAX(SequenceId),0)
FROM Alg.[Sequence] WITH (ROWLOCK)
WHERE CustomerId = @CustomerId

INSERT INTO Alg.[Sequence]
VALUES (
@MaxSeqId + 1
,@CustomerId
,@SendingData
,GETDATE()
)
因此,每当高频插入过程(在带有触发器的表上)发生时,就会出现违反复制键错误的情况。我尝试了
ROWLOCK
,但没有成功。我怎样才能防止这种情况发生

更新


有人问我为什么不使用内置序列,我尝试过,但找不到如何使用组合主键的序列。我不希望SequenceId列是identity,事实上,我希望将SequenceId保留为每个CustomerId的标识。您可以看到我的另一个与此相关的列

这是一个糟糕的设计。你真的应该使用序列。真的

这是一个糟糕的设计的原因之一是,它很容易得到像你有一个错误。下面是如何使代码实际工作:

begin transaction

SELECT @MaxSeqId =  ISNULL(MAX(SequenceId),0)
FROM Alg.[Sequence] WITH (ROWLOCK, UPDLOCK, SERIALIZABLE)
WHERE CustomerId = @CustomerId

INSERT INTO Alg.[Sequence](SequenceId, CustomerId, Data)
VALUES (
@MaxSeqId + 1
,@CustomerId
,@SendingData
)

commit transaction
UPDLOCK指示SQL Server对读取的行设置限制性锁,并忽略版本存储。SERIALIZABLE指示SQL使用范围锁定,这样即使找不到行,也会在密钥范围上使用U锁,以防止并发SELECT在第一个会话执行插入并提交事务之前发现没有行


一个稍微好一点的模式是在序列生成器周围使用应用程序锁,在生成密钥之前调用,然后在之后立即调用。这样并发会话就不必等到第一个会话的事务提交后再生成下一个键值。

这是一个糟糕的设计。你真的应该使用序列。真的

这是一个糟糕的设计的原因之一是,它很容易得到像你有一个错误。下面是如何使代码实际工作:

begin transaction

SELECT @MaxSeqId =  ISNULL(MAX(SequenceId),0)
FROM Alg.[Sequence] WITH (ROWLOCK, UPDLOCK, SERIALIZABLE)
WHERE CustomerId = @CustomerId

INSERT INTO Alg.[Sequence](SequenceId, CustomerId, Data)
VALUES (
@MaxSeqId + 1
,@CustomerId
,@SendingData
)

commit transaction
UPDLOCK指示SQL Server对读取的行设置限制性锁,并忽略版本存储。SERIALIZABLE指示SQL使用范围锁定,这样即使找不到行,也会在密钥范围上使用U锁,以防止并发SELECT在第一个会话执行插入并提交事务之前发现没有行


一个稍微好一点的模式是在序列生成器周围使用应用程序锁,在生成密钥之前调用,然后在之后立即调用。这样,并发会话就不必等到第一个会话的事务提交后再生成下一个键值。

您可以尝试以下intead:

SELECT @MaxSeqId =  ISNULL(MAX(SequenceId),0)
FROM Alg.[Sequence] WITH (ROWLOCK, UPDLOCK, HOLDLOCK)
WHERE CustomerId = @CustomerId

INSERT INTO Alg.[Sequence]
VALUES (
@MaxSeqId + 1
,@CustomerId
,@SendingData
,GETDATE()
)
这将在事务完成之前保持序列表上的锁(范围锁或表锁)。请确保此操作在事务中运行。如果您说这是从触发器内部运行的,那么这应该是隐式的


请注意,以这种方式确定最大ID是无效的。如果您不介意编号中偶尔出现的间隙,那么一个
标识
列就足够了。如果您不需要间隙,也许计数器表可以提供帮助(例如)。

您可以尝试以下方法:

SELECT @MaxSeqId =  ISNULL(MAX(SequenceId),0)
FROM Alg.[Sequence] WITH (ROWLOCK, UPDLOCK, HOLDLOCK)
WHERE CustomerId = @CustomerId

INSERT INTO Alg.[Sequence]
VALUES (
@MaxSeqId + 1
,@CustomerId
,@SendingData
,GETDATE()
)
这将在事务完成之前保持序列表上的锁(范围锁或表锁)。请确保此操作在事务中运行。如果您说这是从触发器内部运行的,那么这应该是隐式的


请注意,以这种方式确定最大ID是无效的。如果您不介意编号中偶尔出现的间隙,那么一个
标识
列就足够了。如果您不需要间隙,也许计数器表可以提供帮助(例如)。

为什么要创建自己的序列表?建议使用序列对象。请在此处查找更多信息:
IDENTITY(1,1)
是否为您执行递增操作,因此您不需要插入它?询问朋友。我已经更新了与问题相关的序列对象。你绝对可以也应该在这里使用序列。我并不假装知道你的目标是什么(我也不理解你的反对意见),但事实上,你正在重新创建一个序列的破碎版本。如果你能(几乎)用你写的代码做到这一点,那么你就可以用序列正确地做到这一点。序列只是获取唯一整数的另一种方法。仅此而已。您仍然可以为每个客户获得一个可用的序列号,碰巧它也是唯一的。为什么要创建自己的序列表?建议使用序列对象。请在此处查找更多信息:
IDENTITY(1,1)
是否为您执行递增操作,因此您不需要插入它?询问朋友。我已经更新了与问题相关的序列对象。你绝对可以也应该在这里使用序列。我并不假装知道你的目标是什么(我也不理解你的反对意见),但事实上,你正在重新创建一个序列的破碎版本。如果你能(几乎)用你写的代码做到这一点,那么你就可以用序列正确地做到这一点。序列只是获取唯一整数的另一种方法。仅此而已。您仍然可以为每个客户获得一个可用的序列号,碰巧它也是唯一的。Note HOLDLOCK相当于可序列化Note HOLDLOCK相当于可序列化谢谢David,我更新了我的问题为什么不能使用序列对象,正如我在更新中指出的,我需要SequenceId作为每个不同CustomerId的标识。我想你的最后一段建议使用序列的方法,这与我的情况一致吗?是的,上面添加额外
UPDLCOK
SERIALIZABLE
的脚本正在工作,然而,我希望看到一个sequence对象的例子,它可以保证每个customerId的sequenceId增量是独立的,不会有任何冲突。@ibubi A不能这样使用,它只是一个序列,不能细分为几个类别(例如,按客户ID)。如果您真的需要这样的构造,我建议您创建一个计数器表(参见中的链接)