開始之前明確一下死鎖和鎖等待這兩個事件的異同
相同的之處:兩者都是當前事物在試圖請求被其他事物已經占用的鎖,從而造成當前事物無法執行的現象
不同的之處:死鎖是相關session雙方或者多方中必然要犧牲(回滾)至少一個事務,否則雙方(或者多方)都無法執行;鎖等待則不然,對于暫時無法申請到的鎖,嘗試持續地“等待一段時間”,這個等待的時間就是“鎖等待”引數決定,超出之后就不等了, 當事物鎖等待超時后,當前事物已經持有的鎖如何處理,是一個非常考究的問題,
對于MySQL來說,可以選擇回滾整個事務,或者是僅回滾當前鎖超時的陳述句,具體參考這里:https://www.cnblogs.com/wy123/p/12724252.html
下文對Postgresql中鎖超時之后當前Session中事務和陳述句的處理進行一個驗證,
Postgresql的死鎖檢測機制
鎖等待有可能是發展成死鎖,也有可能不是死鎖,可能繼續等待一段時間之后就可以正常申請到所需要的鎖了,
發生死鎖的情況下,一定會產生鎖等待,因為此時的鎖等待繼續下去沒有任何意義,所以必須(立刻)犧牲其中一個事務,MSSQL和MySQL都是類似的處理機制,
沒有死鎖等待時間一說,因為一旦死鎖的條件生成,則沒有任何緩沖的余地,必須至少犧牲(回滾)其中一個事物,釋放其占用的鎖,來打斷這個死鎖的倍訓,
但在MySQ中有一個死鎖自動檢測開關,可以打開或者關閉這個自動的死鎖檢測與解除機制,默認是打開的,
但是在Postgresql中,有一個死鎖等待事件的引數,默認是1s中,也就是是說Postgresql后臺行程會以1s的頻率來檢測是否存在死鎖,
換句話說就是,在Postgresql的死鎖檢測機制中,不是以MySQL中那種實時監測方式來處理死鎖的,為什么postgresql中對于死鎖要以類似于定時輪訓的方式來實作死鎖檢測而不是實時監測?
回答這個問題之前,先回到MySQL中:
上面提到MySQL中的死鎖自動檢測機制是有一個開關的,當然這個開關是可以關閉的,也就是系統不檢測死鎖資訊,那么在死鎖發生后也就無法自動犧牲其中一個事務,
那為什么還要允許這個開關設定為關閉呢?其實死鎖檢測也是需要代價的,尤其是實時監測,參考這里:https://mp.weixin.qq.com/s/Lc_tQEK55r_syapebSu0Cg,提到過通過關閉死鎖檢測來提升性能的最佳實踐,
然后回到Postgresql中:
在Postgresql中,deadlock_timeout是進行死鎖檢測之前在一個鎖上等待的總時間(以毫秒計),Postgresql的理念中認為死鎖檢測代價是比較高的,因此服務器不會在每次等待鎖時都判斷有沒有形成死鎖,我們樂觀地假設在生產應用中死鎖是不常出現的,并且只在開始檢測死鎖之前等待一會兒,增加這個值就減少了浪費在無用的死鎖檢測上的時間(頻率),但是降低了報告真正死鎖錯誤的速度,默認是 1 秒(1s),這可能是實際中你想要的最小值,在一個高負載的服務器上,你可能需要增大它,這個值的理想設定應該超過你通常的事務時間,這樣就可以減少在鎖釋放之前就開始死鎖檢查的機會,同理,對于高并發小事務處理系統上,默認的1秒已經足夠了,只有超級用戶可以更改這個設定,
參考:https://postgresqlco.nf/zh/doc/param/deadlock_timeout/
Postgresql鎖超時
Postgresql中同樣可以設定所等待的超時時間,意味著當前事務在請求一個鎖的時候,一旦等待時長超出指定的時間,當前陳述句被中止,該引數的默認值為0,意味著發生鎖等待的時候永遠不超時,一直等待下去,

與statement_timeout不同,這個超時只在等待鎖時發生,注意如果statement_timeout為非零,設定lock_timeout為相同或更大的值沒有意義,因為事務超時將總是先與鎖超時觸發, 官網說不推薦在postgresql.conf中設定lock_timeout,因為它會影響所有會話,
實際這個建議值得商榷,如果不設定lock_timeout,一個Session的鎖等待將無限拉長(如果statement_timeout不限制的話),一旦大量的連接涌入進來等待一個短時間內無法釋放的不兼容鎖,那么資料庫的連接數可能在短時間內被打爆,影響一些正常的連接,這里認為應該根據系統中事務輕重程度,設定成超過deadlock_timeout且小于statement_timeout的一個值,強制一些超鎖等待超時的Session自動終止,不要無限期等待而占用資料庫連接,引發系統級的例外,
Postgresql中的鎖超時后事務的處理
相比MySQL給予了用戶充分的自由,在超等待超時后,可以選擇設定回滾當前陳述句,或者回滾整個事務(引數innodb_rollback_on_timeout決定),Postgresql中是如何處理的呢? 可以創建一個死鎖Session,當前事務作為犧牲品犧牲之后,出現current transaction is aborted, commands ignored until end of transaction block當前Session作為犧牲品之后,回滾的是整個事務,這個容易理解
對于鎖超時之后當前Session的情況呢,設定一個鎖超時的時間
制造一個鎖超時的場景,鎖超時之后,當前Session的任何陳述句都會被回滾,即便是執行一個commit,當前Session鎖超時后的Session狀態,雖然還是一個活動事務,但能且只能回滾,

鎖超時之后的Session狀態
可見,默認情況下,Postgresql在當前Session鎖超時之后,會回滾整個事務,而不是當前陳述句,這樣其實更加的科學合理,MySQL也是這么建議的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/1160.html
標籤:PostgreSQL
