阅读(4522) (10)

PostgreSQL NOTIFY

2021-08-23 15:46:47 更新

NOTIFY — 生成一个通知

大纲

NOTIFY channel [ , payload ]

描述

NOTIFY命令发送一个通知事件以及一个可选的载荷字符串给每个正在监听的客户端应用,这些应用之前都在当前数据库中为指定的频道名执行过LISTEN channel 。通知对所有用户都可见。

NOTIFY为访问同一个PostgreSQL数据库的进程集合提供了一种简单的进程间通讯机制。伴随着通知可以发送一个载荷字符串,通过使用数据库中的表从通知者向监听者传递额外的数据,也可以构建用于传输结构化数据的高层机制。

由一个通知事件传递给客户端的信息包括通知频道名称、发出通知的会话的服务器进程PID以及载荷字符串,如果载荷字符串没有被指定则它为空字符串。

将在一个给定数据库以及其他数据库中使用的频道名称由数据库设计者定义。通常,频道名称与数据库中某个表的名称相同,并且通知事件其实就意味着:我改变了这个表,来看看改了什么吧。但是NOTIFYLISTEN命令并未强制这样的关联。例如,一个数据库设计者可以使用几个不同的频道名称来标志一个表上的不同种类的改变。另外,载荷字符串可以被用于多种不同的情况。

NOTIFY被用来标志对一个特定表的改变时, 一种有用的编程技巧是把NOTIFY 放在一个由表更新触发的语句触发器中。在这种方式中,每当表被改变时 都将自动发生通知,并且应用程序员不可能会忘记发出通知。

NOTIFY以一些重要的方式与 SQL 事务互动。首先,如果一个NOTIFY在一个事务内执行,在事务被提交之前,该通知事件都不会被递送。这是合适的,因为如果该事务被中止,所有其中的命令都将不会产生效果,包括NOTIFY在内。但如果期望通知事件被立即递送,那这种行为就会令人不安。其次,如果一个监听会话收到了一个通知信号而它正在一个事务中,在该事务完成(提交或者中止)之前,该通知事件将不会被递送给它连接的客户端。同样,原因在于如果一个通知在一个事务内被递送且该事务后来被中止,我们会希望该通知能以某种方式被撤销 — 但是服务器一旦把通知发送给客户端就无法收回它。因此通知事件只能在事务之间递送。其中的要点是把NOTIFY用作实时信号的应用应该让它们事务尽可能短小。

如果在同一事务中使用相同的有效负载字符串多次向同一通道名称发送信号,则只向侦听器传递通知事件的一个实例。另一方面,带有不同载荷字符串的通知将总是作为不同的通知被递送。类似地,来自不同事务的通知将不会被折叠成一个通知。除了丢弃后来生成的重复通知实例之外,NOTIFY保证来自同一个事务的通知按照它们被发送的顺序被递送。还可以保证的是,来自不同事务的消息会按照其事务被提交的顺序递送。

一个执行NOTIFY的客户端自己也同时在监听同一个通知频道是很常见的事。在这种情况下,和所有其他监听会话一样,它会取回一个通知事件。根据应用的逻辑,这可能导致无用的工作,例如从自己刚刚写入的一个表中读出相同的更新。可以通过关注发出通知的服务器进程PID(在通知事件消息中提供)与自己的会话PID(可以从 libpq得到)是否相同来避免这种额外的工作。当两者相同时,该通知事件就是当前会话自己发出的,所以可以忽略。

参数

channel

要对其发信号的通知频道的名称(任意标识符)。

payload

要通过通知进行沟通的载荷字符串。这必须是一个简单的字符串。在默认配置下,该字符串不能超过 8000 字节(如果需要发送二进制数据或者更多信息,最好是把它放在一个数据库表中并且发送该记录的键)。

注解

有一个队列保持着已经发送但是还没有被所有监听会话处理的通知。如果该队列被占满,调用NOTIFY的事务将在提交时失败。该队列非常大(标准安装中是 8GB)并且应该足以应付几乎每一种用例。不过,如果一个会话执行了NOTIFY并且接着长时间进入一个事务,不会发生清理操作。一旦该队列使用过半,你将在日志文件中看到警告,它指出哪个会话阻止了清理。在这种情况中,应该确保这个会话结束它的当前事务,这样清理才能够进行下去。

函数pg_notification_queue_usage返回队列中当前被 待处理通知所占据的比例。详见第 9.26 节

一个已经执行了NOTIFY的事务不能为两阶段提交做准备。

pg_notify

要发送一个通知,你也能使用函数pg_notify(text, text)。该函数采用频道名称作为第一个参数,而载荷则作为第二个参数。如果你需要使用非常量的频道名称和载荷,这个函数比NOTIFY命令更容易使用。

例子

psql配置和执行一个监听/通知序列:

LISTEN virtual;
NOTIFY virtual;
Asynchronous notification "virtual" received from server process with PID 8448.
NOTIFY virtual, 'This is the payload';
Asynchronous notification "virtual" with payload "This is the payload" received from server process with PID 8448.

LISTEN foo;
SELECT pg_notify('fo' || 'o', 'pay' || 'load');
Asynchronous notification "foo" with payload "payload" received from server process with PID 14728.

兼容性

在 SQL 标准中没有NOTIFY语句。

参见

LISTEN , UNLISTEN