Вопрос параллельного обновления SQL теста - PullRequest
4 голосов
/ 22 марта 2010

У меня есть база данных SQLServer 2008, в которой у меня есть таблица для тегов. Тег - это просто идентификатор и имя. Определение таблицы тегов выглядит следующим образом:

CREATE TABLE [dbo].[Tag](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](255) NOT NULL
 CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, 
       STATISTICS_NORECOMPUTE  = OFF, 
       IGNORE_DUP_KEY = OFF,      
       ALLOW_ROW_LOCKS  = ON, 
       ALLOW_PAGE_LOCKS  = ON)
)

Имя также является уникальным индексом. далее у меня есть несколько процессов, добавляющих данные в эту таблицу с довольно быстрой скоростью. Эти процессы используют хранимый процесс, который выглядит следующим образом:

ALTER PROC [dbo].[lg_Tag_Insert]
    @Name varchar(255)
AS


DECLARE  @ID int
SET @ID = (select ID from Tag where Name=@Name )

if @ID is null
    begin
            INSERT Tag(Name)
            VALUES (@Name)

            RETURN SCOPE_IDENTITY()

    end
else
    begin
        return @ID
    end

Моя проблема в том, что, помимо того, что я новичок в параллельном проектировании базы данных, кажется, что есть условие гонки, которое заставляет меня иногда получать ошибку, что я пытаюсь ввести дубликаты ключей (Имя) в БД. Ошибка:

Невозможно вставить строку повторяющегося ключа в объект 'dbo.Tag' с уникальным индексом 'IX_Tag_Name'.

Это имеет смысл, я просто не уверен, как это исправить. Если это где код, я бы знал, как заблокировать правильные области. SQLServer - это совсем другой зверь.

Первый вопрос: как правильно кодировать эту «проверку, а затем обновлять шаблон»? Кажется, мне нужно получить эксклюзивную блокировку строки во время проверки, а не общую блокировку, но мне не совсем понятен лучший способ сделать это. Любая помощь в правильном направлении будет принята с благодарностью. Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 22 марта 2010

Я предпочитаю выходные параметры (поэтому я их так закодировал), но это должно преформироваться быстрее всего с наименьшим количеством попаданий в таблицу:

ALTER PROC [dbo].[lg_Tag_Insert]
    @Name varchar(255)
   ,@ID   int  OUTPUT
AS
BEGIN TRY
    SET @ID=NULL
    INSERT Tag (Name) VALUES (@Name)
    SET @ID=SCOPE_IDENTITY()
END TRY
BEGIN CATCH
    SELECT @ID=ID from Tag where Name=@Name
END CATCH

IF @ID IS NULL
BEGIN
    RETURN 1
END

RETURN 0

GO
0 голосов
/ 23 июля 2010

Я нашел лучшие результаты в таблицах с тяжелыми вставками, чтобы установить ограничение «Игнорировать дубликаты» и позволить дубликатам падать на пол при захвате новых вставок.для вашего sproc вы можете полностью исключить тест и вернуть SCOPE_IDENTITY () или ноль после вставки.

0 голосов
/ 22 марта 2010

Правильный код будет:

  • В SP, предпочтительно с сериализуемой транзакцией
  • Сначала выберите в таблице тегов, чтобы получить идентификатор
  • Если ноль, вставить.

Изоляция транзакции обеспечит сериализацию транзакций.

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

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

Что вы могли бы сделать:

  • Используйте отдельные соединения для вставки тегов.
  • Когда вы получаете повторяющуюся ошибку, игнорируйте ее, запросите идентификатор, используйте идентификатор. Поскольку вы находитесь на отдельном соединении, это не имеет значения.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...