NOTIFY

生成一条通知。

概要

NOTIFY <channel> [ , <payload> ]

描述

NOTIFY 命令会向当前数据库中所有曾对指定频道名称执行过 LISTEN <channel> 的客户端应用程序发送通知事件,并可选地附带一个"有效负载"字符串。通知对所有用户可见。

NOTIFY 为访问同一数据库的进程集合提供了一种简单的进程间通信机制。通知可携带有效负载字符串发送,并可通过在数据库中使用表来构建更高层次的机制,从而将结构化数据从通知者传递给监听者。

通知事件传递给客户端的信息包括通知通道名称、通知会话的服务器进程 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() 返回队列中当前被待处理通知占用的比例。

已执行 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