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

DJC*_*sey 6 sql-server concurrency multithreading sql-server-2008

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

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

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

我尝试在事务中坚持操作,但这只会产生数百个死锁.

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

任何帮助,将不胜感激,

谢谢.

Pan*_*vos 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 
Run Code Online (Sandbox Code Playgroud)

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

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


use*_*019 0

问题在于执行选择然后插入,然后通常选择有一个读锁,然后插入有一个写锁。如果没有事务,那么许多更新的时间通常会允许发生多次插入,如您所见。在事务中,第一个读锁将阻止其他进程获取写锁,如果多个进程获取读锁,则没有一个进程可以获得写锁,因此会出现死锁。

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

1)首先执行插入,如果尝试插入重复则处理异常或错误

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

或 3) 如果 RDBMS 允许,可以使用 merge 命令。这将在一个命令中完成所有检查和插入,但始终会更改数据库。

编辑:我认为如果您需要确保插入一条且只有一条记录,因为测试必须在事务中进行,那么没有真正的替代方法 1。

通过检查一笔交易中是否存在,然后在另一笔交易中进行插入和检查,可以降低成本。因此,在大多数情况下,您只有一个选择,而在其他情况下,您会得到完全缓慢的插入和检查,但这种情况应该不会经常发生。