Lock:advisory - Amazon Relational Database Service

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

Lock:advisory

Lock:advisory 事件表示 PostgreSQL 應用程式使用鎖定來協調多個工作階段的活動。

相關的引擎版本

此等待事件資訊與 RDS for PostgreSQL 9.6 版及更新版本有關。

Context

PostgreSQL 諮詢鎖定是應用程式層級的合作式鎖定,由使用者的應用程式碼明確鎖定和解除鎖定。應用程式可以使用 PostgreSQL 諮詢鎖定來協調多個工作階段的活動。不同於物件層級或資料列層級的一般鎖定,應用程式完全控制鎖定的生命週期。如需詳細資訊,請參閱 PostgreSQL 文件中的諮詢鎖定

諮詢鎖定可以在交易結束之前釋放,或由工作階段跨交易保留。但系統強制的隱含鎖定不是如此,例如 CREATE INDEX 陳述式在資料表上取得的存取獨佔鎖定。

如需用來取得 (鎖定) 和釋放 (解除鎖定) 諮詢鎖定的函數描述,請參閱 PostgreSQL 說明文件中的諮詢鎖定函數

諮詢鎖定是在一般 PostgreSQL 鎖定系統上實作,在 pg_locks 系統檢視表中可見。

原因

明確使用此鎖定類型的應用程式可完全控制鎖定。如果查詢為每個資料列都取得諮詢鎖定,可能會造成鎖定激增或長期累積。

當查詢取得的鎖定比查詢傳回的資料列更多時,就會出現這些現象。應用程式最終必須釋放每個鎖定,但如果是在未傳回的資料列上取得鎖定,則應用程式無法找齊全部的鎖定。

下列範例來自 PostgreSQL 文件中的諮詢鎖定

SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100;

在此範例中,只有在內部選取資料列並鎖定其 ID 值之後,LIMIT 子句才能停止查詢的輸出。突然發生這種情況表示資料量不斷增加,導致規劃工具選擇另一個未經過開發期間測試的執行計劃。在此情況下,發生累積起因於應用程式對每個已鎖定的 ID 值,明確呼叫 pg_advisory_unlock。但是,在此情況下,找不到在未傳回的資料列上取得的鎖定集。因為是在工作階段層級取得鎖定,交易結束時不會自動釋放鎖定。

鎖定嘗試受阻次數激增的另一個可能原因是意外衝突。在這些衝突中,應用程式的無關聯部分不慎共用相同的鎖定 ID 空間。

動作

檢閱諮詢鎖定的應用程式用量,並詳述在應用程式流程中何處和何時取得和釋放每一種諮詢鎖定。

查明究竟是工作階段取得太多鎖定,還是長時間執行的工作階段未及早釋放鎖定,導致鎖定逐漸堆積。您可以使用 pg_terminate_backend(pid) 結束工作階段,以免工作階段層級鎖定逐漸堆積。

等待諮詢鎖定的用戶端以 wait_event_type=Lockwait_event=advisory 出現在 pg_stat_activity 中。您可以在 pg_locks 系統檢視表中查詢相同的 pid,尋找 locktype=advisorygranted=f,以取得特定的鎖定值。

然後,您可以在 pg_locks 中查詢 granted=t 的同一個諮詢鎖定,以識別引起封鎖的工作階段,如下列範例所示。

SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid, blocked_activity.usename AS blocked_user, blocking_activity.usename AS blocking_user, now() - blocked_activity.xact_start AS blocked_transaction_duration, now() - blocking_activity.xact_start AS blocking_transaction_duration, concat(blocked_activity.wait_event_type,':',blocked_activity.wait_event) AS blocked_wait_event, concat(blocking_activity.wait_event_type,':',blocking_activity.wait_event) AS blocking_wait_event, blocked_activity.state AS blocked_state, blocking_activity.state AS blocking_state, blocked_locks.locktype AS blocked_locktype, blocking_locks.locktype AS blocking_locktype, blocked_activity.query AS blocked_statement, blocking_activity.query AS blocking_statement FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid != blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT blocked_locks.GRANTED;

所有諮詢鎖定 API 函數都有兩組引數,可能是一個 bigint 引數或兩個 integer 引數:

  • 如果 API 函數有一個 bigint 引數,則前段 32 位元在 pg_locks.classid 中,後段 32 位元在 pg_locks.objid 中。

  • 如果 API 函數有兩個 integer 引數,則第一個引數為 pg_locks.classid,第二個引數為 pg_locks.objid

pg_locks.objsubid 值表示使用何種 API 形式:1 表示一個 bigint 引數;2 表示二個 integer 引數。