PostgreSQL識別阻塞會話
版權宣告:本文為Buddy Yuan原創文章,未經允許不得轉載。原文地址:ofollow,noindex" target="_blank">PostgreSQL識別阻塞會話
在Oracle當中,一般使用v$session中的獲取阻塞資訊(blocking_session),而在PostgreSQL中,我們來看看怎麼識別阻塞會話。我們首先來做個測試。
postgres=# create table t1(id int); CREATE TABLE
現在我們來給表增加一個欄位,我們使用begin來開始事務,但是我們不用end塊來結束。
postgres=# begin; BEGIN postgres=# alter table t1 add column name text; ALTER TABLE
此時在另外一個節點執行插入就會hang住,一直等待,因為修改的DDL語句的事務仍然沒有結束。
postgres=# insert into t1(id) values(1);
我們可以使用postgresql中的pg_stat_activity檢視,可以看到它並不像oracle中的v$session檢視,有blocking_session欄位。而pg是通過backend_type來實現的。
postgres=# \d pg_stat_activity View "pg_catalog.pg_stat_activity" Column|Type| Collation | Nullable | Default ------------------+--------------------------+-----------+----------+--------- datid| oid||| datname| name||| pid| integer||| usesysid| oid||| usename| name||| application_name | text||| client_addr| inet||| client_hostname| text||| client_port| integer||| backend_start| timestamp with time zone ||| xact_start| timestamp with time zone ||| query_start| timestamp with time zone ||| state_change| timestamp with time zone ||| wait_event_type| text||| wait_event| text||| state| text||| backend_xid| xid||| backend_xmin| xid||| query| text||| backend_type| text||| postgres=# select datname,pid,usename,wait_event_type,wait_event,state,query from pg_stat_activity where backend_type = 'client backend' and pid != pg_backend_pid(); datname| pid| usename| wait_event_type | wait_event |state|query ----------+------+----------+-----------------+------------+---------------------+-------------------------------------- postgres | 3231 | postgres | Client| ClientRead | idle in transaction | alter table t1 add column name text; postgres | 3386 | postgres | Lock| relation| active| insert into t1(id) values(1);
(2 rows)
我們可以很容易看到阻塞的物件和他們執行的SQL語句。當然這個是會話數比較少的情況,當會話數比較多,通過檢視pg_stat_activity來識別阻塞的會話會變得很棘手。
當您想知道PostgreSQL中當前持有或者是授權那些鎖的時候,可以檢視pg_locks,主要看RowExclusiveLock和AccessExclusiveLock。
postgres=# \d pg_locks View "pg_catalog.pg_locks" Column|Type| Collation | Nullable | Default --------------------+----------+-----------+----------+--------- locktype| text||| database| oid||| relation| oid||| page| integer||| tuple| smallint ||| virtualxid| text||| transactionid| xid||| classid| oid||| objid| oid||| objsubid| smallint ||| virtualtransaction | text||| pid| integer||| mode| text||| granted| boolean||| fastpath| boolean||| postgres=# select locktype,database,relation,pid,mode,granted from pg_locks where pid != pg_backend_pid(); locktype| database | relation | pid|mode| granted ---------------+----------+----------+------+---------------------+--------- virtualxid||| 3386 | ExclusiveLock| t virtualxid||| 3231 | ExclusiveLock| t relation|13806 |16434 | 3231 | AccessExclusiveLock | t transactionid ||| 3231 | ExclusiveLock| t relation|13806 |16434 | 3386 | RowExclusiveLock| f relation|13806 |16439 | 3231 | AccessExclusiveLock | t relation|13806 |16437 | 3231 | ShareLock| t (7 rows)
上面,可以看到會話3386嘗試訪問行級別鎖,但是沒有授予。這個是當前插入的會話。我們可以把pg_locks與pg_database和pg_class,通過加入pid來獲取更多的資訊,如下所示:
postgres=# select b.locktype,d.datname,c.relname,b.pid,b.mode from pg_locks b,pg_database d,pg_class c where b.pid in (3386,3231) and b.database = d.oid and b.relation = c.oid; locktype | datname| relname | pid|mode ----------+----------+---------+------+--------------------- relation | postgres | t1| 3231 | AccessExclusiveLock relation | postgres | t1| 3386 | RowExclusiveLock (2 rows)
那麼我們如何識別阻塞會話呢?很簡單,使用pg_blocking_pids函式傳遞被堵塞的會話,就可以看到阻塞者。
postgres=# select pg_blocking_pids(3386); pg_blocking_pids ------------------ {3231} (1 row)
然後如果想殺掉這個會話可以使用pg_terminate_backend函式傳入阻塞的源頭會話。然後就可以插入成功了。
postgres=# select pg_terminate_backend(3231); pg_terminate_backend ---------------------- t