Взаимоблокировка при запросе обновления только при более чем двух транзакциях одновременно - PullRequest
2 голосов
/ 28 мая 2020

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

Я разбил проблему на ограничение «UNIQUE NONCLUSTERED» для определения таблицы. Когда я сниму это ограничение, все транзакции будут ждать своих необходимых ресурсов и завершатся sh без ошибок.

Вот мой пример кода для воспроизведения проблемы:

CREATE TABLE [dbo].[profiles]
(
    [ProfileID] [int] IDENTITY(1,1) NOT NULL, 
    [ProfileName] [nvarchar](255) NOT NULL, 
    [GroupFK] [int] NULL

    CONSTRAINT [PK_Profile] 
        PRIMARY KEY CLUSTERED ([ProfileID] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY],
    -- !!! [UI_UniqueNameInGroup] seems problematic for me because i get deadlocks when executing more than two transactions at once !!!
    CONSTRAINT [UI_UniqueNameInGroup] 
        UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
                      WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                            IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                            ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

INSERT INTO profiles (ProfileName) VALUES ('PROFILE_99')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_88')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_77')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_66')

Это образцы моих транзакций:

BEGIN TRAN  
     UPDATE profiles 
     SET ProfileName = 'NewProfile_99' 
     WHERE ProfileID = 4

     WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN  
     UPDATE profiles 
     SET ProfileName = 'NewProfile_66' 
     WHERE ProfileID = 1

     WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN  
     UPDATE profiles 
     SET ProfileName = 'NewProfile_88' 
     WHERE ProfileID = 3

     WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN  
     UPDATE profiles 
     SET ProfileName = 'NewProfile_77' 
     WHERE ProfileID = 2

     WAITFOR DELAY '00:00:05.000'
COMMIT TRAN

Вот мои графики взаимоблокировок:

deadlockgraph1.xdl

deadlockgraph2.xdl

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

Мне также нравится просто получить для этого рабочее решение.

Есть ли решение на моей стороне (без изменения схемы базы данных)?

1 Ответ

1 голос
/ 28 мая 2020

Изменить ограничение [UI_UniqueNameInGroup], чтобы разрешить только блокировки строк:

CONSTRAINT [UI_UniqueNameInGroup] UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
WITH (PAD_INDEX = OFF, 
      STATISTICS_NORECOMPUTE = OFF, 
      IGNORE_DUP_KEY = OFF, 
      ALLOW_ROW_LOCKS = ON, 
      ALLOW_PAGE_LOCKS = OFF
) ON [PRIMARY]

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

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