【问题标题】:How to avoid deadlock while updating a table multiple times in a stored procedure?在存储过程中多次更新表时如何避免死锁?
【发布时间】:2019-12-06 15:44:43
【问题描述】:

我有一个存储过程,我在其中多次更新表,但是当多个会话运行此过程时,这会导致死锁。

我尝试使用 Begin Trans 和 Commit 语句,但对我不起作用。我已经读过创建索引可以解决我的问题,但它有什么帮助以及我应该在哪一列上创建(哪种类型的)索引?

UPDATE lessonPlanSkill SET 
  PreviousProficiency = lessonPlanSkill.CurrentProficiency ,
  PreviousAccuracy = lessonPlanSkill.CurrentAccuracy  ,
  CoveragePercentage = Calculations.CoveragePercentage,
  AccuracyPercentage = Calculations.AccuracyPercentage,
  CurrentAccuracy = Calculations.AccuracyPercentage,
  TotalQuestions = Calculations.AttemptedQuestions,
  CorrectQuestions = Calculations.CorrectAttempts,
  CurrentAchievementScore = Calculations.CurrentAchievedScore,
  TotalAchievementScore = Calculations.TotalAchievementScore,
  TimeSpentInMin=TimeSpentInMin+@ModuleAttemptHistoryTimeSpent,
  AchievementPercentage = CASE WHEN (Calculations.TotalAchievementScore>0) 
  THEN (Calculations.CurrentAchievedScore/Calculations.TotalAchievementScore)*100 
  END
FROM #tblLearnerProficiency Calculations 
INNER JOIN dbo.UserLessonPlanSkill lessonPlanSkill ON lessonPlanSkill.UserId = @pUserId AND lessonPlanSkill.LessonPlanSharedTrackingId = @LessonPlanSharedTrackingId AND lessonPlanSkill.SkillId = Calculations.SkillId AND lessonPlanSkill.IsDeleted = 0

-- Insert statement to insert some data in UserLessonPlanSkill table
-- Again update statement which is most likely causing deadlock with above update statement
UPDATE lessonPlanSkill SET 
  CurrentProficiency = ISNULL(logic.ProficiencyLevel,1),
  ModifiedOn = GETUTCDATE(),
  IsSkillStuck = CASE WHEN (logic.ProficiencyLevel = @PROFICIENT_STATUS) THEN 0 ELSE IsSkillStuck END  
FROM #tblLearnerProficiency Calculations 
INNER JOIN dbo.UserLessonPlanSkill lessonPlanSkill ON 
lessonPlanSkill.UserId = @pUserId AND 
lessonPlanSkill.LessonPlanSharedTrackingId = @LessonPlanSharedTrackingId 
AND lessonPlanSkill.SkillId = Calculations.SkillId AND 
lessonPlanSkill.IsDeleted = 0
INNER JOIN dbo.JudgementLogic logic WITH(NOLOCK) ON logic.FormulaId = 
Calculations.FormulaId AND logic.IsDeleted = 0
WHERE 
Calculations.AccuracyPercentage BETWEEN AccuracyMinPercentage AND 
AccuracyMaxPercentage  AND
Calculations.CoveragePercentage BETWEEN CoverageMinPercentage AND 
CoverageMaxPercentage 

-- After some queries, another update statement comes at the end of this stored procedure updating same table.

UPDATE CurrentResult SET 
  MedianPercentage = ((CoveragePercentage/MaxQuestionCount)*AccuracyPercentage)
FROM dbo.UserLessonPlanSkill CurrentResult  
INNER JOIN #tblFilteredSkills skills ON skills.SkillId = 
CurrentResult.SkillId 
AND CurrentResult.LessonPlanSharedTrackingId = @LessonPlanSharedTrackingId
AND CurrentResult.UserId = @pUserId 
AND CurrentResult.IsDeleted = 0

PS:这里是deadlock graph,这里是从分析器中提取的description

【问题讨论】:

  • 我可以在不创建任何索引的情况下避免死锁吗,因为索引会使我的更新变慢,这是我不想要的。
  • 为什么开始事务和提交对你不起作用?你能详细说明一下吗?创建事务是为了避免死锁,因此它们会起作用。您必须了解它们,即使事务一次只能运行一个 DML - 独占写入!
  • 我为前两个更新语句(即Begin Trans First UpdateStatement SecondUpdateStatement Commit)添加了 Begin Transaction 和 Commit。它适用于同时访问此过程的 4 个多个会话,但是,当我将会话数增加到 20 时,它再次引发死锁异常
  • 死锁是一个庞大的主题,而且没有唯一的答案。索引肯定会有所帮助,因为它们允许更快地访问相关数据。除非您在表上有很多索引,否则您不应该害怕添加更多索引。 SQL Server 是一个高性能数据库,它可以处理一些索引的更新。另一方面,事务对于避免死锁几乎没有什么作用,除非您在正在使用的表上使用排他表锁,这肯定会影响您的性能。
  • @Sham,性能和并发(例如死锁)密切相关。查询和索引调优是避免死锁的第一步。当查询涉及的数据多于需要时(例如,由于缺少有用的索引或不可解析的表达式),死锁更有可能发生。简而言之,有用的索引可以提高整体性能并减少/消除死锁,尽管有额外的开销。

标签: sql sql-server stored-procedures database-deadlocks


【解决方案1】:

我已通过创建临时表、插入所需数据并更新该临时表或将新数据插入该临时表来解决此问题。最终根据主键(索引)使用临时表中的值插入/更新实际表。这解决了我的死锁问题。 PS:如果有人得到更好的东西,请发表评论或添加新的答案!

【讨论】:

    猜你喜欢
    • 2017-10-18
    • 2020-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多