死鎖對於DBA或是數據庫開發人員而言並不陌生,它的引起多種多樣,通常而言,數據庫應用的開發者在設計時都會有必定的考量進而儘可能避免死鎖的產生.但有時由於一些特殊應用場景如高頻查詢,高併發查詢下因爲數據庫設計的潛在問題,一些不易捕捉的死鎖可能出現從而影響業務.這裏爲你們介紹因爲設計問題引發的鍵查找死鎖及相關的解決辦法.數據庫
這裏咱們在測試的同時開啓trace profiler跟蹤死鎖視圖(locks:deadlock graph).(固然也能夠開啓跟蹤標記,或者應用擴展事件(xevents)等捕捉死鎖)併發
建立測試對象code數據庫設計
create table testklup ( clskey int not null, nlskey int not null, cont1 int not null, cont2 char(3000) ) create unique clustered index inx_cls on testklup(clskey) create unique nonclustered index inx_nlcs on testklup(nlskey) include(cont1) insert into testklup select 1,1,100,'aaa' insert into testklup select 2,2,200,'bbb' insert into testklup select 3,3,300,'ccc'
開啓會話1 模擬高頻update操做高併發
----模擬高頻update操做 declare @i int set @i=100 while 1=1 begin update testklup set cont1=@i where clskey=1 set @i=@i+1 end
開啓會話2 模擬高頻select操做測試
----模擬高頻select操做 declare @cont2 char(3000) while 1=1 begin select @cont2=cont2 from testklup where nlskey=1 end
此時開啓會話2執行一小段時間時咱們就能夠看到相似錯誤信息:圖1-1spa
圖1-1設計
而在咱們開啓的跟蹤中捕捉到了以下的死鎖圖.圖1-2code
圖1-2對象
死鎖分析:能夠看出因爲讀進程(108)請求寫進程(79)持有的X鎖被阻塞的同時,寫進程(79)又申請讀進程(108)鎖持有的S鎖.讀執行計劃圖1-3,寫執行計劃圖1-4blog
(因爲在默認隔離級別下(讀提交)讀申請S鎖只是瞬間過程,讀完當即釋放,不會等待事務完成),因此在併發,執行頻率不高的情形下不易出現.但咱們模擬的高頻狀況使得S鎖得到頻率很是高,此時就出現了僅僅兩個會話,一個讀,一個寫就形成了死鎖現象.
圖1-3
圖1-4
死鎖緣由:讀操做中的鍵查找形成的額外鎖(彙集索引)需求
解決方案:在瞭解了死鎖產生的緣由後,解決起來就比較簡單了.
咱們能夠從如下幾個方面入手.
a 消除額外的鍵查找鎖需的鎖
b 讀操做時取消獲取鎖
a.1咱們能夠建立覆蓋索引使select語句中的查詢列包含在指定索引中
CREATE NONCLUSTERED INDEX [inx_nlskey_incont2] ON [dbo].[testklup] ([nlskey] ASC) INCLUDE ( [cont2])
a.2 根據查詢需求,分步執行,經過彙集索引獲取查詢列,避免鍵查找.
declare @cont2 char(3000) declare @clskey int while 1=1 begin select @clskey=clskey from testklup where nlskey=1 select @cont2=cont2 from testklup where clskey=@clskey end
b 經過改變隔離級別,使用樂觀併發模式,讀操做時源行無需鎖
declare @cont2 char(3000) while 1=1 begin select @cont2=cont2 from testklup with(nolock) where nlskey=1 end
結束語.咱們在解決問題時,最好弄清問題的本質緣由,經過問題點尋找出適合本身的環境的解決方案再實施.