Q''*_*Q'' 1 sql-server blocking
我将获得一个由 SQL Server 数据库在运行时分配的 GUID 列表。
我的直接想法是为 GUID 创建一个表。当一批 GUID 被订购时,它们将被插入到表中。
然后,在运行时,将调用 SPROC 从表中选择 GUID。我可以检索下一个 GUID 并立即将其从表中删除,或者检索下一个 GUID 并更新另一列以声明其已使用。
但这让我想知道同时对数据库进行两次调用以获取 GUID 的竞争条件的可能性。在我看来,如果时间完全不方便的话,可以为这两个调用检索相同的 GUID。
Q1:我对这种潜在竞争状况的担忧是否正确?即它会发生吗?
Q2:有没有办法避免这种情况(例如设置一个 SPROC 以在每次调用时阻塞?)
我能想到的一种至少可以使竞争条件失败的方法是设置另一个进行 GUID 分配的表,并将 GUID 列设置为唯一。然后,在运行时过程中,当检索到 GUID 时,它将通过 GUID 分配表分配给产品。如果为两个产品颁发了相同的 GUID,那么当需要分配它们时,其中一个产品将无法分配并被拒绝。
基本上,您正在寻找对队列表中下一行的独占访问。假设有这样的表:
CREATE TABLE dbo.GUIDQueue
(
RowNumber bigint IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
TheGuid uniqueidentifier NOT NULL,
Claimed bit NOT NULL DEFAULT 0
);
-- insert some n number of rows
DECLARE @n int = 1000;
;WITH src(guid) AS
(
SELECT TOP (@n) NEWID()
FROM sys.all_objects AS o
CROSS JOIN sys.all_objects AS s
)
INSERT dbo.GUIDQueue(TheGuid) SELECT guid FROM src;
Run Code Online (Sandbox Code Playgroud)
此过程将序列化删除,而不会阻止另一个会话获取下一个值:
ALTER PROCEDURE dbo.PluckFromQueue
@NextGUID uniqueidentifier OUTPUT
AS
BEGIN
DECLARE @g TABLE(NextGUID uniqueidentifier);
;WITH NextGUID AS
(
SELECT TOP (1) TheGuid
FROM dbo.GUIDQueue WITH (ROWLOCK, READPAST, UPDLOCK)
ORDER BY RowNumber
)
DELETE NextGUID OUTPUT deleted.TheGUID INTO @g;
SELECT @NextGUID = MAX(NextGUID) FROM @g;
END
GO
Run Code Online (Sandbox Code Playgroud)
或者如果您想更新:
ALTER PROCEDURE dbo.PluckFromQueue
@NextGUID uniqueidentifier OUTPUT
AS
BEGIN
DECLARE @g TABLE(NextGUID uniqueidentifier);
;WITH NextGUID AS
(
SELECT TOP (1) RowNumber, TheGuid, Claimed
FROM dbo.GUIDQueue WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE Claimed = 0
ORDER BY RowNumber
)
UPDATE NextGUID SET Claimed = 1
OUTPUT deleted.TheGUID INTO @g;
SELECT @NextGUID = MAX(NextGUID) FROM @g;
END
GO
Run Code Online (Sandbox Code Playgroud)
您可以像这样从队列中获取下一个项目:
DECLARE @guid uniqueidentifier;
EXEC dbo.PluckFromQueue @NextGUID = @guid OUTPUT;
SELECT @guid;
Run Code Online (Sandbox Code Playgroud)
然后用于@guid
您需要执行的任何后续处理。
要测试它是否已序列化,请将其放入两个窗口中:
BEGIN TRANSACTION;
DECLARE @guid uniqueidentifier;
EXEC dbo.PluckFromQueue @NextGUID = @guid OUTPUT;
SELECT @guid;
-- COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
运行一个,然后运行另一个,两者都会得到一个值。不要忘记同时提交两者。
Remus 和 gbn 讨论了为什么您希望对出列查询提供这三个锁定锁定提示。
如果您想在没有这些提示的情况下观察会发生什么(也许这就是您想要的行为),请将它们注释掉,更改过程,然后重复上面的实验 - 您将看到第一个块会阻止第二个块,直到第一个块提交为止。无论你认为时间有多接近,一个人会赢,另一个人会等待。但无论哪种情况,都不要通过将过程调用包装在事务中来加剧这种情况。您想要这样做的唯一原因是您想“撤消”出列操作。如果由于某种情况或异常而最终没有使用队列中的 GUID,那么好吧,将其视为损失(或者您始终可以将该值插入回队列中)。
如上所述,也不要忘记提交第二个事务。
我在这里写了类似的内容(生成一大堆唯一数字的美妙之处,您不必稍后确认它们是唯一的),但在那篇文章中,我没有解决如果您想回滚索赔该怎么办:
您可能会考虑添加某种后台进程来监视队列的大小(或者还剩下多少无人认领的行,在这种情况下,过滤索引可能会很方便),这样您就可以在到达队列时自动补充新行。某个点(而不是在交易中耗尽)。确保您的后台进程以智能方式计算行数。
归档时间: |
|
查看次数: |
180 次 |
最近记录: |