PG 数据备份报错:You might need to increase "max_locks_per_transaction 解决办法

PG 数据备份报错:You might need to increase "max_locks_per_transaction 解决办法

💡 作者:古渡蓝按

个人微信公众号 :微信公众号(深入浅出谈java)

感觉本篇对你有帮助可以关注一下,会不定期更新知识和面试资料、技巧!!!

本文 是对下篇链接文章的后续增强,了解备份可先阅读下篇链接文章

PostgreSQL数据库在Windows上实现异地自动备份指南-喂饭图文教程 - 古渡蓝按 - 博客园

报错信息

备份报错信息:You might need to increase "max_locks_per_transaction".

翻译: 你可能需要增大最大事务锁数量

max_locks_per_transaction 参数用于指定每个事务能获取的锁的数量上限。如果事务需要获取的锁的数量超过了该限制,事务的执行将被终止。通过适当增加 max_locks_per_transaction 参数的值,我们可以避免由于锁限制导致的错误或性能下降。

tex 复制代码
pg_dump: 最后的内置 OID 是 16383
pg_dump: 读扩展
pg_dump: 识别扩展成员
pg_dump: 读取模式
pg_dump: 读取用户定义表
pg_dump: 错误: 查询失败:
提示:  You might need to increase "max_locks_per_transaction".
pg_dump: detail: Query was: LOCK TABLE public.t_1411568720613943894, public.t__776845678375982004, public.t_1318598144, public.t__1084185515, public.t__941540929, public.t__240429387, public.t_1487200566, public.t_990720474, public.t__673777124, public.t__3842978921671954791, public.t__18356806761671954791, public.t__2110790801990202598, public.t__393453906990202598, public.t__1317384021990202598, public.t__727135582990202598, public.t__635583730990202598, public.t_1317302062990202598, public.t__66599909843609561, public.t_195843915143609561, public.t__127787303443609561, public.t_110181100843609561, public.t__210390485943609561, public.t__181974289643609561, public.t_173020648043609561, public.t__159803795743609561, public.t__108891614743609561, public.t__20830166243609561, public.t__118260254643609561, public.t_11034225943609561, public.t_128241074643609561, public.t__60683613543609561, public.t__46536326843609561, public.t_198912020943609561, public.t__629890994375982004, public.t__104504776375982004, public.t__186422411943609561, public.t__11641007043609561, public.t_87765719243609561,

问题原因

在使用postgres执行一个存储过程,存储过程的操作是对全库上百张表添加字段,执行到一半的时候抛出了错误:You might need to increase max_locks_per_transaction。

抛出的异常信息,可以看出是因为公共内存溢出导致的,这个问题在Postgres官方找到了解释:

tex 复制代码
most likely possibility you have a transaction being left open and
accumulating locks.   of course, you have to rule out the fact that
you simply have to increase max_locks_per_transaction: if you have a
lot of tables, it might be reasonable to have to extend this on a
stock config.

one thing that can cause this unfortunately is advisory locks eating
up exactly the amount of shared memory you have.  that's another thing
to rule out.

大意是开启事务后,每次操作一张表,会进行一次表级的lock操作,并增加lock的大小,当lock的大小超过了默认的限定值,就会抛出异常。


共享锁表跟踪在max_locks_per_transaction * (max_connections +     max_prepared_transactions) 个对象(如表)上的锁。 
因此,在任何一个时刻,只有不超过这么多个可区分对象能够被锁住。这个参数控制为每个事务分配的对象锁的平均数量。
个体事务可以锁住更多对象,数量可以和锁表中能容纳的所有事务的锁一样多。这不是能被锁住的行数,那个值是没有限制的。
默认值 64 已经被历史证明是足够的,但是如果你有需要在一个事务中使用很多不同表的查询(例如查询一个有很多子表的父表),你可能需要提高这个值。这个参数只能在服务器启动时设置。

当运行一个后备服务器时,你必须设置这个参数为大于等于主服务器上的值。否则,后备服务器上将不允许查询。

问题原因已经找到,是因为一个过程中操作的表过多,超过了最大对象锁的限制数量,导致了异常。

解决办法

可以先看看 数据库的配置

sql 复制代码
-- postgres中默认max_locks_per_transaction大小是64
show max_locks_per_transaction;

结果:

max_locks_per_transaction --------------------------- 64 (1 row)

解决方案一:修改数据库配置

第一步:修改 PostgreSQL 配置文件 (postgresql.conf)

  • 你需要手动编辑 PostgreSQL 的主配置文件,增加它的"锁容量"。

  • 找到文件路径:

  • 根据你的脚本,PostgreSQL 安装在 E:\ 盘。

  • 请打开文件夹:E:\Program Files\PostgreSQL\17\data\

  • 找到文件:postgresql.conf

  • 编辑文件:右键点击该文件,选择"打开方式" -> "记事本"(或者用 VS Code、Notepad++)。

  • 搜索文件:按 Ctrl + F 搜索关键词:max_locks_per_transaction。你会看到这一行(通常在文件中间偏下的位置):

conf 复制代码
#max_locks_per_transaction = 64

修改操作:

  • 去掉行首的井号 #(取消注释)。
  • 将数值 64 改为 512(为了保险,直接给足空间,这里看情况改为128 或者 512,根据实际情况)。

修改后的样子应该是:

conf 复制代码
max_locks_per_transaction = 512

保存文件:

  • 点击"文件" -> "保存"。
  • 如果提示"拒绝访问",说明你没有管理员权限。请右键点击记事本图标,选择"以管理员身份运行",然后再通过记事本打开并修改该文件。

第二步:重启 PostgreSQL 服务

修改配置文件后,必须重启数据库服务才能生效。

  • 按下键盘上的 Win + R 键。

  • 输入 services.msc 并回车。

  • 在服务列表中找到名为 postgresql-x64-17 (或者名称中包含 PostgreSQL 17 的那一项)。

  • 右键点击它,选择 "重新启动"。

第三步:运行原脚本

服务重启成功后。

不要修改你的备份脚本,直接运行你最原始的脚本(就是没有加 -L 或 --no-locking 参数的那个版本)。

此时,数据库已经拥有了足够的内存来处理这几百张表的锁请求,备份将正常进行,不再报错。

解决方案二:修改备份脚本

如果你无法重启数据库服务,可以通过修改 pg_dump 的参数,让它不要一次性锁定所有表

添加参数:--no-locking 或者 --lock-wait-timeout=10s

修改你的脚本(第3步备份命令):

将原来的这行:

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

修改为(增加了 --lock-wait-timeout=1s 或者直接去掉锁):

注意 :最简单的绕过方法是使用 --no-locking,但这在备份期间如果有 DDL(建表/删表)操作可能会导致备份不一致。鉴于你的表名看起来像系统生成的临时表,通常可以使用此参数。

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v --no-locking -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

或者,如果你希望它等待而不是报错,可以加超时:

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v --lock-wait-timeout=10s -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

使用改方案可能还有报错,需要进一步处理,看下方解决办法

解决办法:使用短参数

报错信息:

tel 复制代码
E:\Program Files\PostgreSQL\17\bin\pg_dump.exe: illegal option -- no-locking
pg_dump: hint: Try "pg_dump --help" for more information.

报错原因:

tex 复制代码
pg_dump.exe: illegal option -- no-locking,这说明你的 PostgreSQL 版本(即使是 17 版本)可能对参数的解析方式比较严格,或者该版本编译时不支持直接使用长参数形式。

修正操作

PostgreSQL 的 pg_dump 通常支持短参数 -L 来表示不加锁。请将你的脚本中执行备份的那一行修改为:

修改前:

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

修改后(增加了 -n 参数):

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v -n -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

或者(如果 -n 不行,尝试 -L):

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v -L -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

参数解释-L--lock-wait-timeout 的短形式有时能绕过长参数的解析错误,或者直接使用 --lock-wait-timeout=1(强制报错而不是挂起)。

推荐直接使用这个命令(强制锁超时,避免死锁也避免参数错误):

batch 复制代码
"%PGPATH%" -h %DB_HOST% -p %DB_PORT% -U %DB_USER% -F c -b -v --lock-wait-timeout=1 -f "%BACKUP_DIR%%BACKUP_FILE%" %DB_NAME%

风险悉知

修改 max_locks_per_transaction 这个参数风险非常小,通常被认为是安全的"常规运维操作"。

你可以放心修改,但为了让你心里有底,我详细解释一下它具体会有什么影响:

1. 唯一的"副作用":内存占用微增

  • 原理:这个参数决定了 PostgreSQL 在启动时,要在内存里预留多大一块地盘来存放"锁"的信息。
  • 实际影响
    • 把数值从 64 改到 512(扩大 8 倍),听起来很多,但实际上占用的内存非常少。
    • 通常来说,这个改动只会导致数据库多消耗 几 MB 到 几十 MB 的内存。
    • 结论 :对于现在的服务器(通常都是几 GB 甚至几十 GB 内存)来说,这点内存消耗几乎可以忽略不计,完全不会影响数据库性能。

2. 生效条件:必须重启

  • 影响 :这个参数属于"静态参数",修改后不会立即生效
  • 操作 :你必须在修改配置文件后,重启 PostgreSQL 服务
  • 风险点:重启会导致数据库连接短暂中断。
    • 建议 :请在业务低峰期(比如凌晨备份时)进行重启操作,避免影响正在使用系统的员工。

3. 极端情况:设置过大(通常不会发生)

  • 如果你把它设置成几百万、几千万,确实会导致数据库启动时申请大量内存,甚至启动失败。
  • 安全范围 :你设置的 512 处于非常安全的范围内(通常建议值在 64 ~ 1024 之间),完全不用担心。

4. 主从架构注意事项(如果你有的话)

  • 如果你的架构是"一主多从"(主库负责写,从库负责读),那么所有从库 的这个参数值,必须 大于或等于 主库的值。
  • 如果你是单机使用(只有一个数据库),则不需要考虑这一点。

📌 总结建议

放心改。

max_locks_per_transaction 设置为 512 是解决备份报错的最标准、最稳妥的方案,且不会对服务器性能造成负面影响。