PostgreSQL死鎖相關

T1YSL發表於2021-08-27

記得原來一老大哥說他運維ORACLE,發現死鎖的時候,總想著緩一緩,再拖一拖,沒準再檢視的時候。死鎖就已經自己釋放掉,不需要處理了(運維的無奈)。而在PostgreSQL中,事務可以按照任意順序加鎖。且PostgreSQL也有著其死鎖處理機制。

當程式請求加鎖的時候,如果失敗,會進入等待佇列。如果在佇列中已經存在一些程式要求本程式中已經持有的鎖,那麼為了儘量避免死鎖,可以把本程式插入到它們的前面。當一個鎖被釋放時,將會試圖喚醒等待佇列裡的程式。這個行為預防了死鎖的產生。

但是這種方法不能完全避免死鎖的產生,PostgreSQL提供如下圖的死鎖檢驗機制
Image.png
且PostgreSQL使用等待圖(WFG)來檢驗死鎖,WFG為一個有向圖,頂點ABC表示申請加鎖的程式,XY有向邊表示依賴關係。
Image.png
圖中的虛線為soft edge,實線為hard edge
當程式A和程式B都在某個鎖的等待佇列,且程式A在程式B的後邊,兩個程式的加鎖要求衝突,程式A在等待程式B,則存在從A到B的有向邊,名為soft edge;如果程式A的加鎖要求和程式B已經持有的鎖衝突,這時候從A指向B的為hard edge。

系統出現死鎖當且僅當WFG出現環,如果WFG中有soft edge環,則可以透過拓撲排序對佇列進行重排,嘗試消除死鎖。從頂點開始,沿著WFG有向邊走,如果能回到頂點,說明出現死鎖。如果路徑沒有出現soft edge,則直接終止此事務。如果存在soft edge,則記錄所有的soft edge,並嘗試對這個集合進行調整。
透過拓撲排序找到可行的方案,則採用此方案,消除死鎖,(不一定是最優的),否則死鎖清除失敗,終止該事務。
以下為pg12.1有向邊的資料結構

/*
 * One edge in the waits-for graph.
 *
 * waiter and blocker may or may not be members of a lock group, but if either
 * is, it will be the leader rather than any other member of the lock group.
 * The group leaders act as representatives of the whole group even though
 * those particular processes need not be waiting at all.  There will be at
 * least one member of the waiter's lock group on the wait queue for the given
 * lock, maybe more.
 */
typedef struct
{
    PGPROC     *waiter;         /* the leader of the waiting lock group */
    PGPROC     *blocker;        /* the leader of the group it is waiting for */
    LOCK       *lock;           /* the lock being waited for */
    int         pred;           /* workspace for TopoSort */
    int         link;           /* workspace for TopoSort */
} EDGE;

PostgreSQL對程式的檢驗過程為:
1.遞迴試圖檢驗和消除死鎖
2.測試當前佇列狀態是否會發生死鎖,如果不滿足約束性檢查,則死鎖。對soft edge調整,並檢驗是否合法。
3.判斷是否出現環,如果存在且不能調整,則死鎖。

相關引數:
1.Postgresql中,有一個死鎖等待事件的引數,預設是1s,也就是是說Postgresql後臺程式會以1s的頻率來檢測是否存在死鎖。
Image.png
鎖等待超時
2.Postgresql中同樣可以設定鎖等待的超時時間,意味著當前事務在請求一個鎖的時候,一旦等待時長超出指定的時間,當前語句被中止。該引數的預設值為0,意味著發生鎖等待的時候永遠不超時,一直等待下去。預設情況下,鎖超時之後,當前Session的任何語句都會被回滾,即便是執行一個commit。
Image.png

以下為標準鎖的鎖模式以及對應的操作
Image.png

鎖手動處理:
1.查詢阻塞:

postgres=# SELECT w.query as waiting_query,
postgres-# w.pid as w_pid,
postgres-# w.usename as w_user,
postgres-# l.query as locking_query,
postgres-# l.pid as l_pid,
postgres-# l.usename as l_user,
postgres-# t.schemaname || '.' || t.relname as tablename
postgres-# from pg_stat_activity w join pg_locks l1 on w.pid = l1.pid
postgres-# and not l1.granted join pg_locks l2 on l1.relation = l2.relation
postgres-# and l2.granted join pg_stat_activity l on l2.pid = l.pid join pg_stat_user_tables t on l1.relation = t.relid;
   waiting_query    | w_pid |  w_user  |            locking_query             | l_pid |  l_user  |   tablename
--------------------+-------+----------+--------------------------------------+-------+----------+----------------
truncate tab_ysl ; |  5391 | postgres | update tab_ysl set id =9 where id=1; |  5524 | postgres | public.tab_ysl
(1 row)
//其中l_pid為阻塞者的pid,w_pid為被阻塞者的pid。

2.查詢表持有的鎖:

postgres=# select  oid  from  pg_class  where  relname= 'tab_ysl';
 oid
------
24580 
(1 row)
使pg_class.oid=pg_locks.relation,則表tab_ysl上持有的鎖為,RowExclusiveLock和
AccessExclusiveLock ,對應了update和truncate的操作。
postgres=# select * from pg_locks where pid in ('5524','5391');
   locktype    | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid  |        mode         | granted | fastpath
---------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+------+---------------------+---------+----------
relation      |    13593 |     2659 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2658 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     1249 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     3455 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2663 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2662 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2685 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2684 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     2615 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
relation      |    13593 |     1259 |      |       |            |               |         |       |          | 7/11               | 5524 | AccessShareLock     | t       | t
virtualxid    |          |          |      |       | 7/11       |               |         |       |          | 7/11               | 5524 | ExclusiveLock       | t       | t
virtualxid    |          |          |      |       | 6/181      |               |         |       |          | 6/181              | 5391 | ExclusiveLock       | t       | t
transactionid |          |          |      |       |            |           512 |         |       |          | 7/11               | 5524 | ExclusiveLock       | t       | f
relation      |    13593 |    24580 |      |       |            |               |         |       |          | 7/11               | 5524 | RowExclusiveLock    | t       | f
transactionid |          |          |      |       |            |           513 |         |       |          | 6/181              | 5391 | ExclusiveLock       | t       | f
relation      |    13593 |    24580 |      |       |            |               |         |       |          | 6/181              | 5391 | AccessExclusiveLock | f       | f
(16 rows)

殺掉阻塞者的程式,釋放鎖:

select  pg_cancel_backend('上面查詢到的阻塞著的pid');

##注意##pg_cancel_backend(‘阻塞者的pid值’);只能殺死select語句, 對其他語句不生效,殺了之後查詢發現還存在,考慮使用pg_terminate_backend(‘程式ID’);


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69990629/viewspace-2789053/,如需轉載,請註明出處,否則將追究法律責任。

相關文章