PostgreSQL出現(xiàn)死鎖該如何解決
什么是數(shù)據(jù)庫(kù)死鎖
在操作系統(tǒng)領(lǐng)域當(dāng)中,死鎖指的是兩個(gè)或者兩個(gè)以上的進(jìn)程在運(yùn)行的過(guò)程中,因?yàn)闋?zhēng)奪共同的訪問(wèn)資源而相互等待阻塞,最終導(dǎo)致進(jìn)程繼無(wú)法續(xù)執(zhí)行的一種阻塞現(xiàn)象。那么在數(shù)據(jù)庫(kù)領(lǐng)域當(dāng)中死鎖又是怎樣的表現(xiàn)形式呢?數(shù)據(jù)庫(kù)死鎖又會(huì)帶來(lái)怎樣的問(wèn)題呢?
在理解數(shù)據(jù)庫(kù)死鎖之前,我們先來(lái)明確下數(shù)據(jù)庫(kù)的鎖到底是什么?有過(guò)Java編程經(jīng)驗(yàn)的同學(xué)都知道,Java中的鎖是為了解決共享數(shù)據(jù)的并發(fā)訪問(wèn)安全問(wèn)題,防止并發(fā)訪問(wèn)導(dǎo)致的共享數(shù)據(jù)出現(xiàn)錯(cuò)亂。那么在數(shù)據(jù)庫(kù)領(lǐng)域,數(shù)據(jù)庫(kù)中的鎖又是來(lái)干什么的呢?實(shí)際上在數(shù)據(jù)庫(kù)中所也是解決并發(fā)問(wèn)題。假如在同一時(shí)刻,可能存在多個(gè)事務(wù)對(duì)同一張表的同一個(gè)字段進(jìn)行數(shù)字的加減操作,如果沒(méi)有任何的控制措施也同樣會(huì)導(dǎo)致各種各樣的數(shù)據(jù)一致性問(wèn)題。因此數(shù)據(jù)庫(kù)的鎖實(shí)際上也是為了保證數(shù)據(jù)一致性的一種手段,對(duì)可能存在的并發(fā)操作進(jìn)行控制。
下面以一個(gè)例子來(lái)進(jìn)行說(shuō)明,假設(shè)有這樣兩個(gè)事務(wù),事務(wù)A中包含如下語(yǔ)句:
UPDATE?user?SET?name = '小慕'?where id?=?1 UPDATE?product?SET?price?=?price?*?10?WHERE?id?=?2
事務(wù)B中包含如下語(yǔ)句:
UPDATE?product?SET?price?=?price?*?100?WHERE?id?=?2 UPDATE?user SET?name?=?'小楓'?WHERE?id?=?1
如果這兩個(gè)事務(wù)并發(fā)執(zhí)行,那么他們可能存在如下的執(zhí)行情況,當(dāng)事務(wù)A執(zhí)行的時(shí)候,首先運(yùn)行了查詢語(yǔ)句:
UPDATE?user?SET?name = '小慕'?where id?=?1
相當(dāng)于事務(wù)A給id為1的數(shù)據(jù)行加上了排他鎖,但是事務(wù)并沒(méi)有執(zhí)行完也就是說(shuō)此時(shí)事務(wù)A持有user表的id為1的排他鎖,排他鎖的特性就是此時(shí)其他事務(wù)不能對(duì)數(shù)據(jù)進(jìn)行刪除和修改,因此只有等待事務(wù)結(jié)束釋放鎖之后才能重新獲取。
此時(shí)事務(wù)B執(zhí)行更新語(yǔ)句獲取了product表id為2的排他鎖,接著事務(wù)B開始執(zhí)行user表的update語(yǔ)句,需要獲取user表的id為1的排他鎖。但是此時(shí)事務(wù)A并未提交,因此事務(wù)A持有表user的id為1的排他鎖,事務(wù)B只有乖乖阻塞等待事務(wù)A釋放鎖。而此時(shí)事務(wù)A執(zhí)行update語(yǔ)句,需要獲取product的id為2的排他鎖,但是此時(shí)事務(wù)B持有該排他鎖,因此也需要等待事務(wù)B鎖釋放。
UPDATE?product?SET?price?=?price?*?10?WHERE?id?=?2
事務(wù)A在等待事務(wù)B結(jié)束釋放鎖,而事務(wù)B又在等待事務(wù)A釋放鎖,最終陷入了互相等待的情況也就是所謂的死鎖。
那么數(shù)據(jù)庫(kù)出現(xiàn)死鎖又會(huì)導(dǎo)致什么問(wèn)題呢?數(shù)據(jù)庫(kù)死鎖會(huì)導(dǎo)致嚴(yán)重的性能問(wèn)題,可能平臺(tái)因?yàn)閿?shù)據(jù)庫(kù)死鎖而導(dǎo)致運(yùn)行緩慢,嚴(yán)重影響用戶正常使用業(yè)務(wù),因此如果出現(xiàn)數(shù)據(jù)庫(kù)死鎖情況需要及時(shí)發(fā)現(xiàn)以及解決。
定位死鎖
//先確定數(shù)據(jù)庫(kù)有沒(méi)有死鎖情況發(fā)生 select?*?from?pg_stat_activity?where?datname?=?'product_db'; //查詢可能鎖了的表的oid select?oid?from?pg_class?where?relname='product'; //查詢對(duì)應(yīng)的pid select?pid?from?pg_locks?where?relation='oid'??//上面查詢出來(lái)的oid //取消或者終止對(duì)應(yīng)的進(jìn)程破壞死鎖條件 select?pg_cancel_backend(pid); select?pg_terminate_backend(pid);
死鎖可能原因及解決辦法
以上分析了PostgreSQL出現(xiàn)死鎖后如何定位分析,那么接下來(lái)就需要總結(jié)分析分析下PostgreSQL出現(xiàn)死鎖情況的原因以及一般的應(yīng)對(duì)解決辦法。
1、索引使用不當(dāng)導(dǎo)致的死鎖問(wèn)題
索引使用存在問(wèn)題的話會(huì)導(dǎo)致死鎖問(wèn)題,假設(shè)在一個(gè)數(shù)據(jù)查詢的事務(wù)當(dāng)中,進(jìn)行數(shù)據(jù)檢索的時(shí)候沒(méi)辦法按照SQL中的where條件進(jìn)行查詢,因此導(dǎo)致了全表掃描,那么此時(shí)數(shù)據(jù)庫(kù)表的行級(jí)鎖會(huì)上升為表級(jí)鎖。如果此時(shí)有多個(gè)未能按照where條件進(jìn)行數(shù)據(jù)查詢的事務(wù)存在,那么就容易導(dǎo)致數(shù)據(jù)庫(kù)死鎖問(wèn)題。也就是說(shuō)在數(shù)據(jù)庫(kù)表數(shù)據(jù)量比較大的時(shí)候,對(duì)應(yīng)進(jìn)行數(shù)據(jù)查詢的表沒(méi)有建立索引或者說(shuō)索引創(chuàng)建的不合理導(dǎo)致無(wú)法通過(guò)索引進(jìn)行數(shù)據(jù)查詢,只能通過(guò)全表索引,這樣的場(chǎng)景下就容易產(chǎn)生死鎖。
如何避免:
在進(jìn)行數(shù)據(jù)查詢的時(shí)候,對(duì)應(yīng)的SQL語(yǔ)句不宜太過(guò)復(fù)雜,也就是說(shuō)盡量避免多張表的關(guān)聯(lián)查詢。
2、不同事務(wù)之間的訪問(wèn)順序問(wèn)題
當(dāng)用戶A 訪問(wèn)數(shù)據(jù)庫(kù)表A時(shí),此時(shí)對(duì)表A加了共享鎖,然后又訪問(wèn)數(shù)據(jù)庫(kù)表B。而此時(shí)另一個(gè)用戶B 訪問(wèn)表B,對(duì)表B加了共享鎖,然后試圖訪問(wèn)表A。但是用戶A由于用戶B已經(jīng)鎖住表B,它必須等待用戶B釋放表B才能繼續(xù),同樣用戶B要等用戶A釋放表A才能繼續(xù),也就是說(shuō)互相等待對(duì)方釋放資源,從而導(dǎo)致了死鎖的發(fā)生。
如何避免:
這種情況在實(shí)際項(xiàng)目中遇到的可能比較多,主要還是需要通過(guò)控制代碼的執(zhí)行邏輯,避免多表操作時(shí)同時(shí)鎖住多個(gè)資源。
避免死鎖的建議
(1)如果平臺(tái)中存在大事務(wù),盡量將其拆分為小事務(wù)。因?yàn)榇笫聞?wù)一般操作的數(shù)據(jù)庫(kù)表或者數(shù)據(jù)都比較多,因此造成死鎖或者阻塞的概率就會(huì)相對(duì)較大。
(2)為數(shù)據(jù)庫(kù)表設(shè)計(jì)合理的索引,盡量避免數(shù)據(jù)查詢時(shí)索引未覆蓋或者索引失效的情況,因?yàn)槿頀呙钑?huì)會(huì)導(dǎo)致給表中的數(shù)據(jù)行上鎖,大大增加了數(shù)據(jù)庫(kù)產(chǎn)生死鎖的概率。
(3)如果業(yè)務(wù)允許,我們可以嘗試將隔離級(jí)別調(diào)低,比如將隔離級(jí)別從RR調(diào)整為RC,可以避免掉很多因?yàn)間ap鎖造成的死鎖。
(4)在我們自己的代碼中,盡量以一致的順序獲取對(duì)象上的鎖,避免事務(wù)中SQL交互執(zhí)行,從而降低死鎖發(fā)生的概率。
附:數(shù)據(jù)庫(kù)中常見的死鎖原因與解決方案
1. 事務(wù)之間對(duì)資源訪問(wèn)順序的交替
出現(xiàn)原因:
一個(gè)用戶A 訪問(wèn)表A(鎖住了表A),然后又訪問(wèn)表B;另一個(gè)用戶B 訪問(wèn)表B(鎖住了表B),然后企圖訪問(wèn)表A;這時(shí)用戶A由于用戶B已經(jīng)鎖住表B,它必須等待用戶B釋放表B才能繼續(xù),同樣用戶B要等用戶A釋放表A才能繼續(xù),這就死鎖就產(chǎn)生了。
解決方法:
這種死鎖比較常見,是由于程序的BUG產(chǎn)生的,除了調(diào)整的程序的邏輯沒(méi)有其它的辦法。仔細(xì)分析程序的邏輯,對(duì)于數(shù)據(jù)庫(kù)的多表操作時(shí),盡量按照相同的順序進(jìn)行處理,盡量避免同時(shí)鎖定兩個(gè)資源,如操作A和B兩張表時(shí),總是按先A后B的順序處理, 必須同時(shí)鎖定兩個(gè)資源時(shí),要保證在任何時(shí)刻都應(yīng)該按照相同的順序來(lái)鎖定資源
2. 并發(fā)修改同一記錄
出現(xiàn)原因:主要是由于沒(méi)有一次性申請(qǐng)夠權(quán)限的鎖導(dǎo)致的。
用戶A查詢一條紀(jì)錄,然后修改該條紀(jì)錄;這時(shí)用戶B修改該條紀(jì)錄,這時(shí)用戶A的事務(wù)里鎖的性質(zhì)由查詢的共享鎖企圖上升到獨(dú)占鎖,而用戶B里的獨(dú)占鎖由于A有共享鎖存在所以必須等A釋放掉共享鎖,而A由于B的獨(dú)占鎖而無(wú)法上升的獨(dú)占鎖也就不可能釋放共享鎖,于是出現(xiàn)了死鎖。這種死鎖比較隱蔽,但在稍大點(diǎn)的項(xiàng)目中經(jīng)常發(fā)生。
解決方法:
a. 樂(lè)觀鎖,實(shí)現(xiàn)寫-寫并發(fā)
b. 悲觀鎖:使用悲觀鎖進(jìn)行控制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫(kù)的鎖機(jī)制實(shí)現(xiàn),如Oracle的Select … for update語(yǔ)句,以保證操作最大程度的獨(dú)占性。但隨之而來(lái)的就是數(shù)據(jù)庫(kù)性能的大量開銷,特別是對(duì)長(zhǎng)事務(wù)而言,這樣的開銷往往無(wú)法承受。
3. 索引不當(dāng)導(dǎo)致的死鎖
出現(xiàn)原因:
如果在事務(wù)中執(zhí)行了一條不滿足條件的語(yǔ)句,執(zhí)行全表掃描,把行級(jí)鎖上升為表級(jí)鎖,多個(gè)這樣的事務(wù)執(zhí)行后,就很容易產(chǎn)生死鎖和阻塞。類似的情況還有當(dāng)表中的數(shù)據(jù)量非常龐大而索引建的過(guò)少或不合適的時(shí)候,使得經(jīng)常發(fā)生全表掃描,最終應(yīng)用系統(tǒng)會(huì)越來(lái)越慢,最終發(fā)生阻塞或死鎖。
另外一種情況是由于二級(jí)索引的存在,上鎖的順序不同導(dǎo)致的
解決方法:
SQL語(yǔ)句中不要使用太復(fù)雜的關(guān)聯(lián)多表的查詢;使用“執(zhí)行計(jì)劃”對(duì)SQL語(yǔ)句進(jìn)行分析,對(duì)于有全表掃描的SQL語(yǔ)句,建立相應(yīng)的索引進(jìn)行優(yōu)化。
總結(jié)
到此這篇關(guān)于PostgreSQL出現(xiàn)死鎖該如何解決的文章就介紹到這了,更多相關(guān)pg數(shù)據(jù)庫(kù)死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
postgreSQL中的row_number() 與distinct用法說(shuō)明
這篇文章主要介紹了postgreSQL中的row_number() 與distinct用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01postgreSQL 使用timestamp轉(zhuǎn)成date格式
這篇文章主要介紹了postgreSQL 使用timestamp轉(zhuǎn)成date格式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01PostgreSQL數(shù)據(jù)庫(kù)事務(wù)出現(xiàn)未知狀態(tài)的處理方法
這篇文章主要給大家介紹了PostgreSQL數(shù)據(jù)庫(kù)事務(wù)出現(xiàn)未知狀態(tài)的處理方法,需要的朋友可以參考下2017-07-07PostgreSQL對(duì)數(shù)組元素聚合基本方法示例
這篇文章主要為大家介紹了PostgreSQL對(duì)數(shù)組元素聚合基本方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08