PostGresql listen与notify命令
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