SQL锁定问题

时间:2014-03-04 15:01:03

标签: sql sql-server tsql sql-server-2008-r2

我在名为“A”的表格中有很多信息。名为“B”的表格为空。

我从表A得到500行,这些行不在表“B”中:

SELECT TOP 500 * FROM A  WHERE 
NOT EXISTS ( 
        SELECT * FROM B  WHERE 
        (
             A.id1=B.id1 AND A.id2=B.id2
        )

)

一切都运作良好,但我有一个问题。

我有一个Web应用程序,它调用该SQL查询。想象一下,200个人一起调用我的应用程序,名为“B”的表中将存在重复项。播种如何锁定这些行?

这是对的吗?

SELECT TOP 500 * FROM A WITH (ROWLOCK)  WHERE 
NOT EXISTS ( 
    SELECT * FROM B WITH (ROWLOCK)  WHERE 
    (
        A.id1=B.id1 AND A.id2=B.id2
    )
)

更清楚:

想象一下,当你打开我的web应用程序,页面调用(例如Servlet或PHP)SQL查询时。

如果很多人一起打开我的应用程序,则存在线程问题。而open查询是将数据写入名为B的表,另一个查询并行执行相同的操作。

线程问题:当一个线程读取500行时,另一个线程可能会读取相同的数据,因为此时第一个线程仍在写入数据但尚未完成。

线程1读取表“A”中的数据: 1,2,3,4,5,6,7 ... 500

线程1将该数据打包到名为“B”的表中: 1,2,3,4,5,6,7,... 495

线程1未完成,线程2读取表“a”中的数据: 496,497 .. 500,...... 995

线程2写 496,497 .. 500,...... 995

然后再次线程1写入数据

495 .. 500

例如

2 个答案:

答案 0 :(得分:1)

一个行锁只会锁定该行 转移到tablelock会影响性能。

我会在InProcess的A中添加一个状态列 然后在事务标记中为true。

所以你的查询也会有A.InProcess = false。

答案 1 :(得分:0)

我需要指出的是,您发布的查询实际上并没有向表B写任何内容。

话虽如此,如果您只想锁定表A中正在处理的行,我建议在表中添加一个状态列,并使用类似于以下内容的存储过程。

DECLARE @Results TABLE
(
    id1 int NOT NULL,
    id2 int NOT NULL  --Assuming these are ints
)

UPDATE TOP (500) A with (ROWLOCK, READPAST)
SET [Status] = 'InProcess'
OUTPUT inserted.id1,
       inserted.id2
    INTO @Results
WHERE [Status] <> 'InProcess'

SELECT * FROM @Results temp
INNER JOIN A
    ON A.id1 = temp.id1 AND A.id2=temp.id2

当我有多个工作人员来到一张桌子寻找要完成的任务时,我使用过这种方法。

这里的关键点是:

  1. SELECT语句不会锁定或阻止,但UPDATE语句会。
  2. ROWLOCK提示仅用于防止表锁。它不会神奇地创建行锁。我们仅提供提示,以便我们不会锁定整个表格。
  3. READPAST提示告诉每个查询它是否遇到行锁,它应该继续下一行而不是等待锁释放。我们提供此提示,以便可以同时执行多个查询。
  4. 我们设置[Status]字段以指示此行正在处理,应该被忽略,直到我们决定重置状态为止(如果有的话)。这可以防止多个查询在锁定释放后达到状态。
  5. 虽然我们使用UPDATE为我们提供了我们想要的锁,但我们仍然需要实际检索一些数据。将OUTPUT语句转换为表变量使我们可以捕获键,以便以后可以合并回表中。
  6. 这里有很多事情,也许你不需要这一切,但这是我发现在防止竞争条件时给予我高并发性的方法。

相关问题