數據庫阻塞和死鎖在程序開發過程經常出現,怎麼樣避免呢?下面通過Demo簡單模擬下,數據庫發生阻塞和死鎖的現象:
一、數據庫阻塞:
數據庫阻塞的現象:第一個連接佔有資源沒有釋放,而第二個連接需要獲取這個資源。如果第一個連接沒有提交或者回滾,
第二個連接會一直等待下去,直到第一個連接釋放該資源爲止。對於阻塞,數據庫無法處理,所以對數據庫操作要及時地提交或者回滾。
Demo:
--創建表
create table tb(id int,createtime date);
--插入測試數據
insert into tb select 1,sysdate from dual;
insert into tb select 2,sysdate from dual;
insert into tb select 3,sysdate from dual;
commit;
第一個連接,不提交或者回滾:
SQL> update tb set id=2 where id=1;
1 row updated
第二個連接,一直在運行:
SQL> update tb set id=2 where id=1;
因爲第一個連接佔有tb表沒有釋放資源,而第二個連接一直在等待第一個連接釋放該資源。
二、數據庫死鎖:
數據庫死鎖的現象:第一個連接佔有資源沒有釋放,準備獲取第二個連接所佔用的資源,而第二個連接佔有資源沒有釋放,準備獲取第一個連接所佔用的資源。這種互相佔有對方需要獲取的資源的現象叫做死鎖。對於死鎖,數據庫處理方法:犧牲一個連接,保證另外一個連接成功執行。
Demo:
--創建測試表t1
create table t1(id int,createtime date);
insert into t1 select 1,sysdate from dual;
insert into t1 select 2,sysdate from dual;
insert into t1 select 3,sysdate from dual;
commit;
--創建測試表t2
create table t2(id int,createtime date);
insert into t2 select 1,sysdate from dual;
insert into t2 select 2,sysdate from dual;
insert into t2 select 3,sysdate from dual;
commit;
第一個連接,在command窗口中運行:
begin
--先修改t1
update t1 set id=2
where id=1;
--等待20s
dbms_lock.sleep(20);
--再修改t2
update t2 set id=2
where id=1;
end;
/
運行結果:
ORA-00060: 等待資源時檢測到死鎖
ORA-06512: 在 line 9
第二個連接:
begin
--先修改t2
update t2 set id=2
where id=1;
--等待20s
dbms_lock.sleep(20);
--再修改t1
update t1 set id=2
where id=1;
end;
/
運行結果:
PL/SQL procedure successfully completed
因爲第一個連接佔有表t1,想要獲取表t2的資源,而第二個連接佔有表t2,想要獲取表t1的資源,這種互相佔有對方想要獲取的資源,滿足死鎖現象。最後第一個連接報異常退出,而第二個連接執行成功。
死鎖所在的資源和檢測:
在SQL Server的兩個或多個任務中,如果某個任務鎖定了其他任務試圖鎖定的資源。會造成這些任務的永久阻塞,從而出現死鎖。
下圖爲例:
l 事務T1獲得了行R1的共享鎖。
l 事務T2獲得了行R2的共享鎖。
l 然後事務T1請求行R2的排它鎖,但是T2完成並釋放其對R2的共享鎖之前被阻塞。
l T2請求行R1的排它鎖,但是事務T1完成並釋放其對R1持有的共享鎖之前被阻塞。
現在T2與T1相互等待,導致了死鎖。一般情況下監視器會自動檢測並解決這個問題。
數據庫阻塞和死鎖的區別
可以發生死鎖的資源:
死鎖不僅僅發生在鎖資源上面,還會發生在一下資源上:
l 鎖。例如頁、行、元數據和應用程序上的鎖。
l 工作線程。如果排隊等待線程的任務擁有阻塞所有其他工作線程的資源,也會導致死鎖。
l 內存。當併發請求等待獲得內存,而當前的可用內存無法滿足其需要時,可能發生死鎖。
l 並行查詢執行的相關資源。當一個語句用到多個線程運行時,線程之間有可能發生死鎖。
死鎖檢測:
默認5秒鐘搜索SQL Server中的所有任務,檢測是否有死鎖。如果有,將選擇一個作爲犧牲品,並返回1205錯誤。一般是開銷最小的事務作爲犧牲品。
死鎖與阻塞的差別:
阻塞:當一個事務請求一個被其他事務鎖定的資源上的鎖時,發出請求的事務會一直等待下去,知道該鎖被別人釋放,自己能申請到位置。
默認情況下除非設置了LOCK_TIMEOUT,否則事務會一直等待下去。
死鎖:兩個或多個進程之間的相互等待。但是由於SQL Server有數據庫引擎死鎖檢測方案,至少5秒鐘會消除一個現有的死鎖。對性能的影響往往沒有阻塞嚴重。
問題定位:
1、 跟蹤標誌1204和跟蹤標誌1222:
打開跟蹤的語句:
DBCC TRACEON(1222,-1)
DBCC TRACEON(1204,-1)
數據庫阻塞和死鎖的區別
對於1222產生的結果解釋:
1、 死鎖犧牲的進程:第一句deadlockvictim=processXXXX,中的xxxx就是死鎖犧牲品。
2、 死鎖發生的進程信息:第二部分的process-list
數據庫阻塞和死鎖的區別
3、 發生死鎖的資源信息:在結果的resource-list中
數據庫阻塞和死鎖的區別
4、 死鎖圖形事件:
從sqlserver profiler中得到,一般結合1222跟蹤標誌和sql trace。
首先從errorlog中尋找1222的輸出結果,根據輸出的時間在跟蹤裏找到相應的連接。然後分析原因。
解決辦法:
儘管死鎖不能完全避免,但是可以把機會降到最低:
l 按同一順序訪問對象。
l 避免事務中的用戶交互。
l 保持事務簡短並處於一個批處理中。
l 使用腳底的隔離級別。
l 調整語句的執行計劃,減少鎖的申請數目。
按同一順序訪問對象:
如果所有併發事務按同一順序訪問對象,則發生死鎖的可能性會降低。
避免事務中的用戶交互:
避免編寫包含用戶交互的事務,因爲沒有用戶干預的批處理的運行速度遠快於必須等待用戶響應時的查詢速度。
保持事務簡短並處於一個批處理中:
運行時間越長,等待時間就越長,造成死鎖的機會就越高。
使用腳底的隔離級別:
確定事務能否在低隔離級別上運行。儘可能使用較低的隔離級別。
調整語句的執行計劃,減少鎖的申請數目:
可以從執行計劃中找出哪些資源耗得比較多。此時鎖的數目也會相應增多。