Postgresql的Listen-Notify機制

Postgresql提供 監聽 - 通知 的訂閱服務,使得數據庫客戶端向服務端的指定通道註冊爲監聽客戶端,服務端在發出Notify通知時,所有已註冊的監聽客戶端將能夠收到該通知。

使用示例

服務端

-- 不帶消息的通知
users=> NOTIFY test_channel;
NOTIFY

-- 帶消息的通知
users=> NOTIFY test_channel, 'I am payload';
NOTIFY

客戶端

-- 不帶消息的通知
users=> LISTEN test_channel;
LISTEN
Asynchronous notification "test_channel" received from server process with PID 9501.

-- 帶消息的通知
users=> LISTEN test_channel;
LISTEN
Asynchronous notification "test_channel" with payload "I am payload" received from server process with PID 9501.

注意

  • 上面的例子看起來並不像發佈-訂閱,因爲客戶端LISTEN後並不會被主動通知,而是每次要服務端NOTIFY後,客戶端再去LISTEN,這其實是誤會——Postgresql客戶端檢測通知事件的方式取決於該客戶端底層應用的編程接口。支持同步輪詢異步通知兩種方式。很多第三方庫是支持異步通知的。
  • 通道名可以使任意有效字符串,通道不需要預先創建。

命令介紹

LISTEN

LISTEN channel (channel值任意)
  • 命令幹了啥

    將當前會話註冊爲一個channel的監聽者,如果當前會話已經註冊,則啥都不幹

  • 何時被通知

    無論何時當命令 NOTIFY channel 被調用時,註冊在該channel上的所有監聽者都會被通知。

  • 合適終止監聽

    當調用UNLISTEN或會話關閉時,監聽終止

  • 監聽原理

    客戶端檢測通知事件使用的方法取決於它使用的 PostgreSQL 應用程序編程接口。

    • 如果使用libpq庫,只是相當於定期執行SQL命令調用PQnotifies方法查詢是否有新的通知
    • 其它接口如libpgtcl提供更高級的方法來處理通知
  • 事務相關性

    LISTEN命令在事務提交時生效,如果執行LISTEN的事務最終回滾了,則LISTEN不會生效。

NOTIFY

NOTIFY channel [, payload] (payload默認要求小於8000字節, 且必須是常量)
  • 命令幹了啥

    NOTIFY向每一個在channel上註冊的客戶端發送通知,通知可附帶可選的字符串載荷payload。通知對所有用戶都是可見的。

  • 通知信息包括

    • 通知的channel名
    • 發出通知的服務端的進程PID
    • 可選的payload字符串,沒有時則爲空字符串
  • channel命名規則

    channel沒有具體的命名規則。一般來說,channel會和某個表名一致,這樣對該channel發出通知在語義上表示“我修改了這個表,看看有什麼新內容吧”(配套的是將這樣的NOTIFY語句放入表更新的觸發器中)。不過還是要具體情況具體分析

  • 事務相關性

    如果NOTIFY用於事務中,則只有當事務提交時纔會真正執行通知,如果事務取消則壓根不會通知。

    如果一個待接收通知的監聽會話正處於事務中,則通知事件會等到它的事務處結束後在發送到它。這是合理的,因爲通知發送了就不能取消,如果在事務中發送或接收,遇到想要取消通知的情況就沒辦法了。

    這樣的缺點導致了實時性不好,因此如果要求實時性高,就要把事務寫短一點。

  • 通知摺疊規則

    • 一個事務中對同一個channel發送多個paylaod一樣的通知,可能會被摺疊成一個通知
    • 一個事務中對同一個channel發送不同的payload通知,不會執行摺疊操作
    • 不同事務,無論channel和payload是否重複,都不會執行摺疊操作
  • 順序

    NOTIFY保證順序

    • 同一事務中,通知發出的順序按NOTIFY聲明的順序進行
    • 不同事務,通知發出的順序按事務提交的順序走
  • 避免額外的工作

    在一個客戶端上發送NOTIFY命令,同時該客戶端監聽同樣的channel,會出現通知發給自己的情況,此時可以通過通知中帶的進程PID發現發送者就是自己,從而忽略本來要執行的邏輯,節省性能。

  • 隊列

    有一個隊列用於存儲已經NOTIFY但還沒被所有註冊的客戶端獲取的通知,如果隊列滿了,則NOTIFY在提交時會失敗。

    該隊列默認大小爲8GB,足夠大多數情況。

    如果一個監聽會話長時間處於事務中,則對應通知會持續累積,當隊列滿了一半時,會打出警告日誌。

    pg_notification_queue_usage函數可以查看用了多少。

  • pg_notify(channel, payload)

    該方法執行同樣的事情,好處是payload不再要求必須是常量,可以通過變量運算得到。

UNLISTEN

UNLISTEN { channel | *)}

用於將當前會話移除出channel的監聽列表,*表示移除當前會話監聽的所有channel。

在會話期間,使用UNLISTEN channel的方式移除監聽

在會話結束時,會自動執行UNLISTEN *

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章