PostGresql listen與notify命令
- 2022 年 8 月 2 日
- 筆記
- PostgreSQL
LISTEN與NOTIFY命令
PostgreSQL提供了client端和其他client端通過伺服器端進行消息通訊的機制。這種機制 是通過LISTEN和NOTIFY命令來完成的。
1.LISTEN與NOTIFY的簡單示例
接下來舉例說明LISTEN和NOTIFY的使用方法。
先運行一個psql(這裡稱為「session1」),執行LISTEN命令,示例如下:
osdba@osdba-laptop:~$ psql psql (9.4betal) Type "help"for help. osdba=# listen osdba; LISTEN
上面的命令「listen osdba」中的「osdba’」是一個消息通道名稱,實際上也可以是其他任 何字元串。
再運行另一個psq1(這裡稱為「session2」),執行NOTIFY命令,示例如下:
osdba=# notify osdba,'hello world'; NOTIFY
NOTIFY命令後的消息通道名稱要與前面的LISTEN命令後的消息通道名稱一致。
此時,sessionl還沒有反應,在sessionl上隨便運行一條命令,示例如下:
osdba=# select 1; ?c01 umn? --------------------- 1 (1 row) Asynchronous notification "osdba" with payload "hello world" received from server process with PID 9872.
可以看到最後一行顯示收到一個非同步消息” Asynchronous notification “osdba” with payload “hello world”….”。
2. LISTEN 與 NOTIFY 的相關命令
LISTEN 與 NOTIFY 的相關命令及兩數主要有以下幾個。
口 LISTEN:監聽消息通道。
口 UNLISTEN: 取消先前的監聽。
口 NOTIFY:發送消息到消息通道中。
口 pg_notity():與NOTIFY 命令的功能相同,也可以發送消息到消息通道中。
口 pg_listening_ channels():調用此兩數可以查詢當前 session 己註冊了哪些消息監聽。
下面講解每個命令的用法。先看 LISTEN 命令,示例如下:
LISTEN channel
此命令比較簡單,後面跟一個通道名稱。如果當前會話已經被註冊為該消息通道的監聽器,再次執行 LISTEN 命令,此消息通道的命令不會報錯,相當於什麼也不做。註冊消息監聽後,如果不想再收到相應的消息,可以使用 UNLISTEN 命令取消監聽,UNLISTEN 命令的語法格式如下:
UNLISTEN { channel | * }
使用特殊的條件通配符「*」可以取消對當前會話所有監聽的註冊。
下面介紹 NOTIFY 命令,示例如下:
NoTIFY channel [ ,’ payload ” ]
NOTIFY 命令發送一個通知事件,同時可以帶一個可選的消息資訊字元串到每個客戶端應用程式,這些應用程式已經預先為當前資料庫的指定名稱的通道執行 LISTEN channel 命令。如果上面的命令沒有指定消息資訊字元串,則消息資訊宇符串是空字元串。
也可以使用西數 pg_notify() 來發送通知事件,此兩數的語法格式如下:
pg notify(text, text)
第一個參數是消息通道的名稱,第二個參數是要發送的消息資訊字元串。
調用兩數 pg_listening_ channels() 查詢當前 session 註冊的消息監聽,命令如下:
osdba=# listen osdba1; LISTEN osdba=# listen osdba2; LISTEN osdba=# select pg_listening_ channels(); pg_listening_channels ------------------------------------------------------- osdba1 osdba2 (2 rows)
3. LISTEN與 NOTIFY 的使用詳解
多個session 可以同時監聽同一個消息通道。當發送端發送一個消息時,所有監聽者都可 能收到此消息。示例如下。
先運行 一個psql (這裡稱為 sessionl」),執行LISTEN 命令,命令如下:
osdba@osdba-laptop:~$ psql psql(9.4betal) Type "help" for help. osdba=# listen osdba: TISTEN
再運行另一個psal (這裡稱為 「session2」 ),執行 LISTEN 命令,命令如下:
osdba@osdba-laptop:~$ psql psql (9. 4betal) Type "help" for help. osdba=# listen osdba; LISTEN
運行另一個 psql(這裡稱為 「session3」),執行 NOTIFY 命令,命令如下:
osdba@osdba-laptop:~$ psql psql(9.4betal) Type "help" for help. osdba=# notify osdba, 'hello world1'; NOTIFY osdba=# notify osdba, 'hello world2' ; NOTIFY osdba=#
這時,在 session1 上隨便運行一條命令,命令如下:
osdba=# select 1; ?column? ----------------------------- 1 (1 row) Asynchronous notification "osdba" with payload "hello world1" received from server process with PID 31453. Asynchronous notification "osdba" with payload process with PID 31453. "hello world2" received from server osdba=#
在 session2 上隨便運行一條命令,命令如下:
osdba=# select 1; ?column? ---------------------------------- 1 (1 row) Asynchronous notification "osdba" with payload "hello world1" received from server process with PID 31453. Asynchronous notification "osdba" with payload "hello world2" received from server process with PID 31453.
從上面的運行結果中可以看到 sessionl 和 session2 都可以接收到此消息。
如果在事務中調用 NOTIFY 發送消息,實際上消息在事務提交時才會被發送,如果事務 回滾了,消息將不會被發送,示例如下 先運行一個 psql (這裡稱為 “session1” ),執行LISTEN 命令,命令如下:
osdba=# listen osdba;
LISTEN
再運行另一個 psql(這裡稱為 “session2″),在此窗口中運行如下命令:
osdba=# begin; BEGIN osdba=# notify osdba, 'hello world'; NOTIFY
上面啟動了一個事務,然後調用了 NOTIFY 發送消息,但事務沒有提交。然後再到 session1 中隨便運行一條命令,可以看到並沒有收到消息,命令如下:
osdba=# select 1; ? column? --------------------- 1 (1 row)
如果再到 session2 中提交事務,命令如下:
osaba=# begin; BEGIN osdba=# notify osdba, 'hello world'; NOTIFY osdba=# end; COMMIT
這時再回到 session1 中隨便運行一條命令,就可以看到收到了消息,命令如下:
osdba=# select 1; ?column? ---------------------------- (1 row) Asynchronous notification "osdba" with payload "hello world" received from server process with PID 31501
另外,使用pg_notity 函數也可以發送消息,還是前面的示例,在session2 中使用 pg_notify發送消息,命令如下。
osdba=# begin; BEGIN osdba=# select pg_notify('osdba', 'pg notify send') ; pg_notify ---------------------- (1 row) osdba=# end; COMMIT
然後在 session1 中隨便運行一條命令就收到了pg_notify()函數發送過來的消息,命令 如下:
osdba=# select 1; ?column? ------------------------------- 1 (1 row) Asynchronous notification "osdba" with payload "pg_notify send" received from server process with PID 31501.
如果在一個事務中發送兩條消息通道名稱相同、消息字元串也完全相同的消息,實際上 只有一條消息會被發送出去,示例如下。 先運行- 一個psql(這裡稱為 ” session1 」),執行 LISTEN 命令,命令如下:
osdba=# listen osdba;
LISTEN
再運行另- 一個psql(這裡稱為 「session2」),在此窗口中運行如下命令:
osdba=# begin; BEGIN osdba=# notify osdba, 'hello world'; NOTIFY osdba=# notify osdba, 'hello world'; NOTIFY osdba=# end; COMMIT
再到 session1 中隨便運行一條命令,看能收到幾條消息,命令如下:
osdba=# select 1; ?column? ------------------------ (1 row) Asynchronous notification "osdba" with payload "hello world" received from server process with PID 10637.
從上面的示例中可以看到,只收到了 一條消息,由此驗證了前面的結論:同一個事務中 的重複消息會自動去重。
NOTIFY 能保證來自同一個事務的資訊按照發送時的順序交付,也能保證來自不同事務 的資訊會按照事務提交的順序交付。
消息隊列持有被發送但是未被監聽會話處理的消息,這些消息太多會導致該隊列變滿, 此時調用 NOTIFY 命令會在提交時發生失敗。不過隊列通常都很大,在默認安裝中是8GB, 所以一般不太會滿。然而,如果一個會話執行 LISTEN 後,長時間處於一個事務中,不清理 消息就可能導致隊列變滿。
注意: 在兩階段提交中不能使用 NOTIFY 命令,示例如下:
osdba=# begin; BEGIN osdba=# notify osdba, hello world'; NOTIFY osdba=# PREPARE TRANSACTION 'myxid'; ERROR: NOTIFY cannot PREPARE a transaction that has executed LISTEN, UNLISTEN, or NOTIFY