У меня есть данные о мертвых блокировках, но я не могу понять, почему они происходят

Я получаю много мертвых блокировок в моем большом веб-приложении.

Как автоматически повторно выполнить заведенную в тупик транзакцию? (ASP.NET Сервер MVC/SQL)

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

Таким образом, я провел целый день с SQL Profiler, устанавливая ключи трассировки и т.д. И это - то, что я получил.

Существует a Users таблица. У меня есть очень высокая применимая страница со следующим запросом (это не единственный запрос, но это - то, которое доставляет неприятности),

UPDATE Users
SET views = views + 1
WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID)

И затем на ВСЕХ страницах существует следующий запрос:

User = DB.Users.SingleOrDefault(u => u.Password == password && u.Name == username);

Это - то, где я получаю Пользователя от cookie.

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

Это - информация от.XDL графика, полученного SQL Profiler (Это - просто первая мертвая блокировка, это не единственное. Целый список является гигантским.):


    
        
            
                
                    
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------     
                    
unknown     
                
                
                
            
            
                
                    
UPDATE Users
    SET Views = Views + 1
    WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID)     
                    
EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0    
                    
unknown     
                
                
(@p0 int,@RETURN_VALUE int output)EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0   
            
            
                
                    
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------     
                    
unknown     
                
                
*password--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
            
        
        
            
                
                    
                
                
                    
                
            
            
                
                    
                
                
                    
                
            
            
                
                    
                
                
                    
                
            
        
    

Я читал много о мертвых блокировках... И я не понимаю, почему это вызывает мертвую блокировку.

Так, очевидно, оба из этого запрашивают выполненный очень часто. По крайней мере, однажды секунда. Возможно, еще чаще (300-400 пользователей онлайн). Таким образом, они могут быть выполнены одновременно очень легко, но почему это вызывает мертвую блокировку? Помогите.

Спасибо

6
задан Community 23 May 2017 в 12:19
поделиться

3 ответа

Вам нужно захватить график тупиков. Присоедините профилировщик и запишите класс График тупиковых ситуаций . Сохраните график .XDL и добавьте эту информацию в свой пост.

До тех пор совершенно очевидно, что ваш запрос DB.Users.SingleOrDefault требует индекса по крайней мере по имени, если не по имени и паролю:

CREATE INDEX idxUsersNamePassword on Users(Name,Password);

Я ожидаю, что у пользователей уже есть индекс по идентификатору, а у статей есть индекс на ArticleID, который также охватывает AuthorID. Предполагая, что Users.ID и Articles.ArticleID являются PK в соответствующих таблицах, они, вероятно, являются кластеризованным ключом соответствующего, так что это правда. Однако стоит дважды проверить.

И, как я уже отвечал вам однажды в вашем предыдущем посте, вы решили продолжить и оставить без ответа, вам следует подумать о включении Изоляции снимков :

ALTER DATABASE ... SET READ_COMMITTED_SNAPSHOT ON

Кроме того, сохранение пароля в открытом виде текст - крупный #fail.

Обновление после информации о тупике

Есть три процесса (запроса):

  • A) ... F048, на котором выполняется SELECT ... FROM Users WHERE Password = ... and Name =. ..
  • B) ... 0988, на котором выполняется SELECT ... FROM Users WHERE Password = ... and Name = ...
  • C) ...FB88, который выполняет UPDATE ...

Цикл взаимоблокировки:

  1. C ожидает блокировки страницы IX, заблокирован блокировкой S A
  2. B ожидает блокировки страницы S, заблокирован C IX lock
  3. A ожидает ресурсов параллельного обмена, блокируется B

Цикл, следовательно, C-> A-> B-> C.

Из того факта, что два задействованных оператора SELECT решают 1) использовать параллельный план и 2) использовать блокировки страниц, очевидно, что они выполняют сквозное сканирование всей таблицы Users. поэтому проблема, как я и предсказывал, заключается в отсутствии индекса (Имя, Пароль) для пользователей, что приводит к тому, что запрос сканирует слишком много данных. Добавление индекса превратит SELECT в прямой поиск по индексу Nc и поиск по кластеризованному индексу, и это резко сократит окно перекрытия с UPDATE. Прямо сейчас UPDATE почти гарантированно конфликтует с всеми SELECT, поскольку каждый SELECT гарантированно читает каждую строку.

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

Для будущей масштабируемости обновление столбца «Просмотры» при каждом просмотре страницы не будет работать. Отложенное обновление, пакетное обновление совокупного счетчика, вертикальное разбиение таблицы Users и удаление столбца Views являются жизнеспособными альтернативами.

11
ответ дан 8 December 2019 в 18:32
поделиться

В этой ситуации (т. Е. Тип данных, которые вы читаете, и характер обновлений, происходящих с этими данными), я бы запустил поисковый запрос пользователя при изолированном чтении незафиксированных.

В качестве альтернативы, более сложное изменение. Судя по опубликованному вами описанию, я бы подумал, что не следует вести подсчет просмотров в пользовательской записи. Вместо этого я бы записал ViewCount для статьи, а затем получил бы общее количество просмотров для пользователя из суммы Articles.ViewCount по AuthorID.

1
ответ дан 8 December 2019 в 18:32
поделиться

Ваша проблема имеет много параллелей со следующей Diagnosing Deadlocks in SQL Server 2005

(Linq to SQL, Read Only transaction being deadlocked by a Read Write transaction)

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

1
ответ дан 8 December 2019 в 18:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: