Вставить проблему параллелизма - многопоточная среда - PullRequest
6 голосов
/ 24 июня 2010

У меня проблема с тем, что одна и та же хранимая процедура вызывается в одно и то же время с точно такими же параметрами.

Цель хранимой процедуры - извлечь запись, если она существует, или создать и извлечь запись, если она не существует.

Проблема в том, что оба потока проверяют существование записи и сообщают о ложном, а затем оба вставляют новую запись, создавая дубликат в базе данных.

Я пытался закрепить операции внутри транзакции, но это просто привело к сотням тупиков.

Можно ли как-нибудь проверить наличие записи потокобезопасным способом, чтобы второй поток не выполнял чтение до тех пор, пока первый не завершит вставку? У меня нет контроля над самими потоками, только над сохраняемыми процессами, которые они выполняют.

Любая помощь будет оценена,

Спасибо.

Ответы [ 4 ]

8 голосов
/ 24 июня 2010

Хитрость заключается в том, чтобы добавить WHERE в оператор INSERT, чтобы 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 были успешными одновременно.

EDIT : синтаксис INSERT ... SELECT необходим, поскольку TSQL не допускает значения VALUES и WHERE.участие в операторе INSERT.

0 голосов
/ 17 августа 2012

Я предполагаю, что вы используете c # для связи с сервером sql, тогда вы можете попробовать изучить параллелизм задач и библиотеку задач для многопоточности хранимых процедур.

0 голосов
/ 24 июня 2010

Не уверен, есть ли в SQL Server. Но в MySQL и в oracle вы можете получить блокировку записи при выполнении выбора, используя для обновления синтаксис.

select * 
from table 
for update

Поскольку другим потокам также требуется блокировка записи при выполнении выбора, они будут ждать, пока первый поток завершит транзакцию.

0 голосов
/ 24 июня 2010

Проблема заключается в выполнении выбора, а затем вставки, затем обычно есть блокировка чтения для выбора, а затем блокировка записи на вставке. Без транзакции время многих обновлений часто позволяет выполнить несколько вставок, как вы видите. В транзакции первая блокировка чтения остановит другие процессы, получающие блокировку записи, и если более одного процесса получит блокировку чтения, то ни один не сможет получить блокировку записи, и вы получите тупик.

В этом случае я бы изменил код вставки так, чтобы индексы позволяли работать только одной вставке, т. Е. У вас есть уникальный ключ, и только один процесс не сможет вставить данные, чтобы не было дубликатов. Процесс обновления в транзакции либо

1) сначала выполнить вставку и устранить исключение или ошибку, если попытается вставить дубликат

или 2) выполнить HOLD LOCK (Sybase и SQL Server) при первом выполнении выбора - таким образом первый блокирующий получает полное разрешение на вставку при необходимости

или 3) Возможно, используйте команду слияния, если СУБД это позволяет. Это выполнит проверку и вставит все в одну команду, но всегда изменит базу данных.

EDIT: Я думаю, что реальной альтернативы 1 не существует, если вам нужно убедиться, что в транзакцию вставлена ​​одна и только одна запись, которая должна быть в транзакции.

стоимость может быть снижена путем проверки наличия forst в одной транзакции, а затем, во-вторых, путем вставки и проверки в другой транзакции. Таким образом, в большинстве случаев у вас просто есть выборка, а в других - полная медленная вставка и проверка, но это должно происходить реже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...