症状
这个错误自带说明书:ERROR: out of shared memory 的 HINT 明说“增加 max_locks_per_transaction”,并报告“这条命令试图锁定 4128 个对象”——分区舰队加索引加约束的总和。预算算术:锁表总槽位是 max_locks_per_transaction × (max_connections + max_prepared_transactions),单事务独占时最多可用整张表,但其他会话持锁时就碰撞。默认每事务 64 为 OLTP 设计,对 500 分区表的 DETACH 完全不够。触发场景集中在多分区 ALTER、单事务触碰数千对象、迁移工具把大量 DDL 包进一个事务。
诊断思路
先读 HINT 拿到实际持有的锁槽位数,超额程度一目了然;再统计单条 DDL 会触碰的对象数(pg_class 里 relispartition 计数 × 依赖级联)乘并发事务数做尺寸计算;最后核对当前配置与默认值的差距。
根因
PostgreSQL 按 max_locks_per_transaction × 连接槽位数预分配固定大小的锁表。单个事务需要锁定的对象超过预算——典型是跨数百分区或外键依赖级联的 DDL——耗尽共享锁表,以 out of shared memory 失败。
修复与验证
两条路按成因选:扇出是意外的(工具把所有 DDL 包一个事务)就重构——分批提交、DETACH 与 DROP 分事务、迁移工具按 50 个一批开有界事务;扇出是内在的(整舰队单条 ATTACH)就调参——max_locks_per_transaction 提到 512 覆盖 500 分区级联(需重启)。验证时重跑此前失败的 DDL 确认完成,再做并发事务负载测试确认锁表无压力。调参会永久增大共享内存锁表,从实测对象数推导预算,不要恐慌式翻倍。
代码示例
ERROR: out of shared memory:
HINT: Another command is in progress, or you have run out of
shared memory for locks: increase max_locks_per_transaction.
This command appears to be trying to lock 4128 objects.
# postgresql.conf — requires restart
max_locks_per_transaction = 512 # covers 500-partition cascades
-- batch the DDL so each transaction stays small:
BEGIN;
ALTER TABLE events DETACH PARTITION events_202610;
COMMIT;
BEGIN;
DROP TABLE events_202610;
COMMIT;
# migration tooling: bounded transactions, not mega-transactions
for batch in chunked(partitions, size=50):
with conn.transaction():
for p in batch:
conn.execute(f"ALTER TABLE events DETACH PARTITION {p}")