【发布时间】: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