PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

NOTIFY

NOTIFY — 生成一个通知

Synopsis

NOTIFY channel [ , payload ]

描述

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

NOTIFY 为访问同一个 PostgreSQL 数据库的一组进程提供了一种简单的进程间通信机制。可以在通知中附带一个载荷字符串,并且可以通过使用数据库中的表把额外的数据从通知者传递给监听者,从而构建出传递结构化数据的更高级机制。

针对一个通知事件传递给客户端的信息包括通知通道名、发出通知的会话的服务器进程 PID,以及载荷字符串(如果载荷未指定,则它是一个空字符串)。

在一个给定的数据库中将使用哪些通道名以及每个通道名的含义,由数据库设计者来定义。通常,通道名与数据库中某个表的名字相同,而通知事件本质上意味着“我更改了这个表,来看看有什么新东西”。但是 NOTIFY 和 LISTEN 命令并不强制任何这样的关联。例如,数据库设计者可以使用几个不同的通道名来表示对单个表的不同种类的更改。或者,也可以使用载荷字符串来区分各种情况。

当 NOTIFY 被用来表示对一个特定表的更改发生时,一种有用的编程技术是把 NOTIFY 放在一个由表更新触发的语句级触发器中。这样,当表被更改时通知就会自动发生,应用程序员不可能意外地忘记做这件事。

NOTIFY 以一些重要的方式与 SQL 事务交互。首先,如果一个 NOTIFY 在一个事务内被执行,那么除非且直到该事务被提交,否则通知事件不会被递送。这是合适的,因为如果事务被中止,其中的所有命令都不会产生任何效果,NOTIFY 也包括在内。但是,如果有人期望通知事件被立即递送,这可能会让人不安。其次,如果一个正在监听的会话在事务内收到一个通知信号,那么直到该事务完成(提交或者中止)之后,通知事件才会被递送给它所连接的客户端。同样,其理由是:如果一个通知在一个后来被中止的事务内被递送,人们会希望这个通知能以某种方式被撤销 — 但服务器一旦把通知发送给客户端,就无法“收回”它。因此,通知事件只在事务之间被递送。由此带来的结果是,使用 NOTIFY 进行实时信令的应用应该尽量让它们的事务保持简短。

如果在同一个事务中用相同的载荷字符串对同一个通道名多次发信令,则只有一个通知事件实例会被递送给监听者。另一方面,带有不同载荷字符串的通知总是会被作为不同的通知递送。类似地,来自不同事务的通知也绝不会被合并成一个通知。除了丢弃重复通知的后续实例之外,NOTIFY 保证来自同一个事务的通知会按照它们被发送的顺序被递送。还可以保证来自不同事务的消息会按照事务提交的顺序被递送。

执行 NOTIFY 的客户端自己也在同一个通知通道上监听,这种情况很常见。在这种情况下,它会收到一个返回的通知事件,就像所有其他正在监听的会话一样。取决于应用逻辑,这可能导致无用的工作,例如,读取一个数据库表去寻找该会话刚刚写入的相同更新。通过留意发出通知的会话的服务器进程 PID(在通知事件消息中提供)是否与自己会话的 PID(可以从 libpq 获得)相同,就可以避免这种额外的工作。当它们相同时,该通知事件是自己工作弹回来的结果,可以忽略。

参数

channel

要被发信令的通知通道的名称(任意标识符)。

payload

要随通知一起传送的“载荷”字符串。它必须被指定为一个简单的字符串字面量。在默认配置下,它必须短于 8000 字节。 (如果需要传送二进制数据或大量信息,最好把它放入一个数据库表并发送该记录的键。)

注记

有一个队列保存着已被发送但尚未被所有监听会话处理的通知。如果这个队列变满,调用 NOTIFY 的事务将在提交时失败。这个队列相当大(在标准安装中为 8GB),对于几乎所有的用例来说它的尺寸都应该是足够的。但是,如果一个会话执行了 LISTEN 然后进入一个很长的事务,就无法进行清理。一旦队列半满,你将会在日志文件中看到警告,指出阻止清理的会话。在这种情况下,你应该确保该会话结束它当前的事务,以便清理可以进行。

函数 pg_notification_queue_usage 返回队列当前被待处理通知占用的比例。更多信息请参见 Section 9.27。

已执行过 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, max_notify_queue_pages