插入并发问题 - 多线程环境

时间:2010-06-24 04:54:41

标签: sql-server multithreading sql-server-2008 concurrency

我遇到一个问题,即使用完全相同的参数在同一时间调用相同的存储过程。

存储过程的目的是获取记录(如果存在),或者创建并获取记录(如果它不存在)。

问题是两个线程都在检查记录是否存在并报告为false,然后两者都插入一条新记录,在数据库中创建一个副本。

我尝试将操作粘贴在事务中,但这只会产生数百个死锁。

有没有什么方法可以用线程安全的方式检查记录是否存在,这样第二个线程在第一个完成插入之前就不会读取它?我无法控制线程本身,只能控制它们正在执行的存储过程。

任何帮助将不胜感激,

感谢。

4 个答案:

答案 0 :(得分:8)

诀窍是在INSERT语句中添加一个WHERE,这样INSERT只有在项不存在时才有效,后跟SELECT语句。假设您可以通过ID列标识记录:

INSERT INTO MyTable (ID,Col1,Col2,...) 
SELECT @IDValue,@Col1Value,@Col2Value, ...
WHERE NOT EXISTS (SELECT ID  
              FROM MyTable 
              WHERE ID=@IDValue) 

SELECT *  
FROM MyTable 
Where ID=@IDValue 

您不需要将语句放在事务中,因为每个语句都在其自己的隐式事务中执行。因此,两个INSERTS无法同时成功。

编辑:INSERT ... SELECT语法是必需的,因为TSQL不允许INSERT语句中的VALUES和WHERE部分。

答案 1 :(得分:0)

麻烦在于做一个选择然后插入然后通常有一个读锁定用于选择,然后插入一个写锁定。如果没有事务处理,那么许多更新的时间通常会允许多次插入发生,如您所见。在一个事务中,第一个读锁定将阻止其他进程获得写锁定,如果多个进程获得读锁定,则没有一个进程可以获得写锁定,从而导致死锁。

在这种情况下,我会更改插入代码,以便索引只允许一个插入工作,即您有一个唯一的键,只有一个进程将能够插入数据,因此不会重复。 然后,更新过程是在事务中

1)首先执行插入操作,如果尝试插入重复的

,则处理异常或错误

或2)在首先执行选择时执行HOLD LOCK(Sybase和SQL Server) - 因此第一个锁定获取在需要时插入的完全权限

或3)如果RDBMS允许,可能使用merge命令。这会检查并在一个命令中插入所有命令但总是会更改数据库。

编辑: 我认为如果你需要确保只插入一条记录,那么就没有真正的替代方法,因为测试必须在交易中。

可以通过检查一个事务中的存在forst然后再执行插入并检入另一个事务来削减成本。因此,在大多数情况下,您只需要一个选择,而在其他情况下,您可以获得完整的慢速插入和检查,但这种情况应该不常发生。

答案 2 :(得分:0)

不确定SQL Server是否拥有它。但是在MySQL和oracle中,你可以在使用 for update 语法进行选择时获得写锁定。

select * 
from table 
for update

由于其他线程在执行select时也需要写锁定,因此它们将等到第一个线程完成事务。

答案 3 :(得分:0)

我假设您正在使用c#与sql server进行通信,然后您可以尝试查看任务并行性和任务库,以便对存储过程进行多线程处理。