REPLICA IDENTITY NOTHING 后仍无法 DELETE,逻辑复制需要什么行标识
无主键表发布 DELETE 时,REPLICA IDENTITY NOTHING 不会绕过行标识检查。核对主键、可用唯一索引或 FULL,并评估订阅端应用与回退影响。
表没有主键,加入发布 DELETE 的逻辑复制 publication 后,删除语句报错:
cannot delete from table "items" because it does not have a replica identity and publishes deletes
把表改为 REPLICA IDENTITY NOTHING,仍不能删除。NOTHING 的含义是不记录这类旧行标识;它不会允许发布端跳过标识检查,也不是“删除只在本地执行”的开关。
错误可以先发生在发布端
订阅端要删除对应行,需要知道发布端删的是哪一行。没有足够的 replica identity 时,发布端会拒绝这类 UPDATE 或 DELETE;此时应核对原事务是否已经失败,不能把它描述成“发布端删除成功,订阅端没收到”。
INSERT 不需要同样的旧行标识,因此插入能同步,不代表删除也满足条件。DEFAULT 通常使用主键;没有主键的 DEFAULT、明确设置的 NOTHING,以及指定索引已经被删除的 USING INDEX,都可能无法提供所需标识。
在发布端检查实际表的设置。下面的 app.items 是示例表名:
SELECT c.oid::regclass AS table_name, c.relreplident
FROM pg_class AS c
WHERE c.oid = 'app.items'::regclass;
relreplident 中的 d、n、i、f 分别对应 DEFAULT、NOTHING、指定索引与 FULL。再核对该表实际所属 publication 及其发布操作,不要只查其中一个 publication:同一张表可以被加入多个 publication。
已有唯一键时,先核对它是否适用
有合适主键的表,可以采用 DEFAULT。使用另一个唯一索引作标识时,该索引需要满足相应限制:唯一、非部分、不可延迟,索引列具有 NOT NULL 等要求。普通索引、可空的唯一列或只覆盖部分行的索引,不能直接当作可用的 replica identity。
以下是切换到已有合格索引的语法示例:
ALTER TABLE app.items REPLICA IDENTITY USING INDEX items_identity_idx;
items_identity_idx 必须替换为已经核对过的实际索引,不能只照名字创建一个空有“唯一”标签的索引。操作需要相应表所有权,属于表结构维护;保存原设置,确认锁等待、并发写入与维护窗口后再安排。
同时检查订阅端对应表的行标识和结构。发布端使用非 FULL 标识时,订阅端也需要具有由相同或更少列组成的合适标识;不能只把发布端的报错消掉,就认定订阅端查找和应用一定正确。
没有合适键时,评估 FULL 的成本
REPLICA IDENTITY FULL 将整行作为标识,可用于没有其他合适键的场景。但它增加旧行信息,并可能使订阅端查找行的成本上升;不能为了一个报错批量把所有表改成 FULL。
例如,在确认数据类型与复制方案适用后,某一张表可以按维护计划调整:
ALTER TABLE app.items REPLICA IDENTITY FULL;
PostgreSQL 18 对订阅端可用于 FULL 行查找的索引有具体规则;有普通索引不代表一定能使用。没有合适查找路径时,大表上的更新或删除应用可能很慢,还需评估实际数据类型与比较能力。不同版本的查找能力不能一概而论。
如果业务本来只需要复制插入,可以另行设计 publication 的发布操作。但停止发布删除会改变订阅端数据语义,不能把它作为“修好删除同步”的办法;还要处理此前与此后两端数据是否一致。
用已提交的业务变更检查两端
修改后,在确认的验证环境中检查发布端删除事务能够提交,订阅端对应行也按预期删除,且没有新的 apply 错误。还应观察复制延迟、WAL 量和订阅端查询成本,判断所选标识是否适合实际负载。
先前已经失败的事务不会因为改了标识而自动重放,应由业务的事务或重试流程处理。回退原 replica identity 设置后,原来的删除限制也可能重新出现;结构设置回退不会自动撤销两端已经提交的数据变更。


