У меня есть проблема, что та же хранимая процедура вызывается в точно то же время с точно тем же paramenters.
Цель хранимой процедуры состоит в том, чтобы выбрать запись, если она существует или создать и выбрать запись, если она не существует.
Проблема состоит в том, что оба потока проверяют существование записи и сообщают о лжи, и затем оба вставляют новую запись, создавая дубликат в базе данных.
Я пытался засунуть операции в транзакции, но это просто произвело сотни мертвых блокировок.
Есть ли какой-либо способ, которым я могу проверить на существование записи ориентированным на многопотоковое исполнение способом так, чтобы второй поток не делал своего чтения, пока первое не закончило свою вставку? Я не имею никакого контроля над самими потоками, только над сохраненным procs, который они выполняют.
Любая справка ценилась бы,
Спасибо.
Уловка состоит в том, чтобы добавить WHERE к вашему оператору INSERT, чтобы INSERT работал, только если элемент не существует, за которым следует оператор SELECT. Предположив, что запись может быть идентифицирована по столбцу идентификатора, вы должны написать:
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 одновременно.
EDIT : синтаксис INSERT ... SELECT необходим, потому что TSQL не допускает частей VALUES и WHERE в операторе INSERT.
Не уверен, есть ли он у SQL Server. Но в MySQL и в Oracle вы можете получить блокировку записи при выборе, используя синтаксис для обновления .
select *
from table
for update
Поскольку другим потокам также требуется блокировка записи при выборе, они будут ждать, пока первый поток не завершит транзакцию.
Проблема заключается в выполнении выбора и затем вставки, тогда обычно существует блокировка чтения для выбора, а затем блокировка записи на вставке. Как видите, без транзакции время выполнения многих обновлений часто позволяет выполнить несколько вставок. В транзакции первая блокировка чтения остановит другие процессы, получившие блокировку записи, и если более одного процесса получают блокировку чтения, тогда ни один из них не может получить блокировку записи, и вы получите тупик.
В этом случае я бы изменил код вставки так, чтобы индексы позволяли работать только одной вставке, т.е. у вас есть уникальный ключ, и только один процесс сможет вставлять данные, поэтому дубликаты не будут.В этом случае процесс обновления выполняется в транзакции либо
1) сначала выполните вставку и обработайте исключение или ошибку, если она попытается вставить дубликат
, либо 2) выполните HOLD LOCK (Sybase и SQL Server) при выполнении выбор первым - таким образом, первый блокируемый получает полное разрешение на вставку при необходимости
или 3) Возможно использование команды слияния, если это позволяет СУБД. Это выполняет проверку и вставку все в одной команде, но всегда будет изменять базу данных.
РЕДАКТИРОВАТЬ: Я думаю, что нет реальной альтернативы 1, если вам нужно убедиться, что есть одна и только одна запись, вставленная в качестве проверки, которая должна быть в транзакции.
Стоимость может быть снижена путем проверки наличия forst в одной транзакции, а затем, во-вторых, выполнения вставки и проверки в другой транзакции. Таким образом, в большинстве случаев у вас просто есть выбор, а в других случаях вы получаете полную медленную вставку и проверку, но это должно происходить реже.