pgbouncer笔记 2
文章目录说明1. 如何判断 pgx 是否发送 Named Prepared Statement方法1开启 PostgreSQL 日志推荐如果 pgx 使用 Named Statement如果是 unnamed如果 Simple Protocol方法2查看 pg_prepared_statements方法3抓 PostgreSQL 协议包最准确NamedUnnamed2. 方案 A稳定优先PgBouncer 配置Go代码3. 方案 B性能优先PgBouncerGo代码4. 两个方案对比5. 针对你当前环境的建议第一步第二步对于方案 A 与方案 B 你列举的例子中没有用到 PrepareContext 与 ExecContext, 我期望使用到这两个函数, 使用这两个函数后, 方案 A 与方案 B 还是否成立?先给结论一、先明确三种“Prepare”不是一回事二、方案 A PrepareContext 会发生什么三、方案 B PrepareContext 会怎样四、那么 PrepareContext 有没有办法在 transaction 模式使用条件1条件2五、那 GORM 的 PrepareStmt 呢推荐六、如果你希望同时使用 PrepareContext有没有推荐方案目标1稳定目标2极致性能七、重新修正之前两个方案八、结合你们项目GORM PgBouncer transactionGo 程序大量使用 PrepareContext ExecContext 为了能不改应用代码 也不会报错 是不是只能使用 pgdog 连接池工具了说明PgBouncer 1.25 transaction 模式支持 Extended Protocol也支持协议级 named Prepared Statement通过 max_prepared_statements但它不是完整的 PostgreSQL session prepared statement 语义因此不能保证所有驱动、所有 prepared statement 使用方式都兼容。1. 如何判断 pgx 是否发送 Named Prepared Statement这是定位你之前stmt xxx does not exist的关键。pgx v5 有多种 QueryExecMode模式是否创建 Named Prepared Statement协议QueryExecModeSimpleProtocol❌Simple QueryQueryExecModeExec❌每次 Parse unnamedExtendedQueryExecModeCacheStatement✅Extended Named PreparedQueryExecModeDescribeExec❌ExtendedQueryExecModeCacheDescribe❌缓存 DescribeExtended方法1开启 PostgreSQL 日志推荐postgresql.conflog_statement all log_min_duration_statement 0重载SELECTpg_reload_conf();如果 pgx 使用 Named Statement日志类似LOG: execute stmtcache_1: SELECT * FROM users WHERE id$1 DETAIL: parameters: $1 10或者LOG: execute lrupsc_1_12345: SELECT ...看到execute xxx:其中 xxx 不是unnamed说明✅ named prepared statement如果是 unnamed日志LOG: execute unnamed: SELECT * FROM users WHERE id$1说明Extended Protocol但是❌ unnamed statement如果 Simple Protocol日志LOG: statement: SELECT * FROM users WHERE id10没有execute方法2查看 pg_prepared_statements直接连接 PostgreSQLselect*frompg_prepared_statements;例如结果name | statement ----------------------------------------------- lrupsc_1_12345 | SELECT * FROM users WHERE id$1说明当前 backend 有named prepared statement。注意如果通过 PgBouncer transaction你查询的时候必须连接到同一个 backend。否则可能为空。方法3抓 PostgreSQL 协议包最准确tcpdumptcpdump\-ieth0\port5432\-wpg.pcapWireshark看NamedParse Statement: lrupsc_1_12345 SQL: select...UnnamedParse Statement: (empty)2. 方案 A稳定优先目标GORM pgx PgBouncer transaction 不要使用 Prepared Statement架构GORM | database/sql | pgx | Extended Protocol | PgBouncer(transaction) | PostgreSQL注意不是 Simple Protocol。这里使用QueryExecModeExec即Parse Bind Execute但是每次重新 Parse。PgBouncer 配置pgbouncer.ini[databases] testdb host127.0.0.1 port5432 dbnametestdb [pgbouncer] pool_mode transaction max_prepared_statements 0 max_client_conn 1000 default_pool_size 50关键max_prepared_statements 0关闭 PgBouncer prepared statement tracking。Go代码go.modrequire(gorm.io/gorm gorm.io/driver/postgres)main.gopackagemainimport(fmtgorm.io/driver/postgresgorm.io/gorm)typeUserstruct{IDuintgorm:primaryKeyNamestringAgeint}funcmain(){dsn:host127.0.0.1 usertestuser password123456 dbnametestdb port6432db,err:gorm.Open(postgres.New(postgres.Config{DSN:dsn,// 方案A// 不使用 Simple// 使用 pgx Exec 模式PreferSimpleProtocol:false,}),gorm.Config{// GORM层关闭缓存PrepareStmt:false,},)iferr!nil{panic(err)}db.AutoMigrate(User{})// insertuser:User{Name:Tom,Age:20,}db.Create(user)// updatedb.Model(User{}).Where(id?,user.ID).Update(age,30,)// selectvarresult User db.Where(id?,user.ID,).First(result)fmt.Println(result)}此时一次查询Parse Bind Execute下一次还是Parse Bind Execute不会Bind existing statement所以不会出现prepared statement does not exist3. 方案 B性能优先目标使用 pgx statement cache PgBouncer prepared statement virtualizationPgBouncerpgbouncer.ini[pgbouncer] pool_mode transaction max_prepared_statements 500 max_client_conn1000 default_pool_size50注意不要server_reset_query_always1不要强制DISCARD ALLGo代码这里需要明确GORM 的PreferSimpleProtocol不够表达CacheStatement所以推荐显式创建 pgx config。完整packagemainimport(fmtgithub.com/jackc/pgx/v5github.com/jackc/pgx/v5/stdlibgorm.io/driver/postgresgorm.io/gorm)typeUserstruct{IDuintgorm:primaryKeyNamestringAgeint}funcmain(){dsn:postgres://testuser:123456127.0.0.1:6432/testdbconfig,err:pgx.ParseConfig(dsn)iferr!nil{panic(err)}// pgx层 config.DefaultQueryExecModepgx.QueryExecModeCacheStatement sqlDB:stdlib.OpenDB(*config)db,err:gorm.Open(postgres.New(postgres.Config{// 使用已有连接Conn:sqlDB,},),gorm.Config{// GORM层 PrepareStmt:false,},)iferr!nil{panic(err)}db.AutoMigrate(User{})user:User{Name:Tom,Age:20,}db.Create(user)db.Model(User{}).Where(id?,user.ID).Update(age,40,)varresult User db.Where(id?,user.ID,).First(result)fmt.Println(result)}此时第一次Parse statementl rupsc_xxx Bind Execute第二次Bind ExecutePgBouncerclient stmt ↓ backend stmt4. 两个方案对比方案A方案BGORM PrepareStmtfalsefalsepgx模式ExecCacheStatementPgBouncer max_prepared_statements0500Extended Protocol✅✅Named Prepared❌✅性能中高稳定性最高较高transaction pool非常适合需要验证5. 针对你当前环境的建议你现在PostgreSQL 15PgBouncer 1.24/1.25GORMpgx v5transaction pooling曾经出现stmt xxx does not exist我建议测试顺序第一步先跑方案APrepareStmtfalse QueryExecModeExec max_prepared_statements0如果稳定说明问题一定来自 prepared statement 生命周期。第二步再切方案BCacheStatement max_prepared_statements500如果方案B失败那么基本可以确定PgBouncer pgx statement cache 兼容性问题而不是 GORM 或 PostgreSQL 问题。这两个方案对比测试一次基本就能把问题定位。对于方案 A 与方案 B 你列举的例子中没有用到 PrepareContext 与 ExecContext, 我期望使用到这两个函数, 使用这两个函数后, 方案 A 与方案 B 还是否成立?先给结论如果代码中显式使用PrepareContext()stmt,err:db.PrepareContext(ctx,sql)那么方案 A不成立或者说目标失效方案 B也不成立至少不再是 pgx CacheStatement 的控制路径原因PrepareContext()是database/sql层主动要求创建 Prepared Statement它绕过了 pgx 的DefaultQueryExecMode决策。这是非常关键的区别。一、先明确三种“Prepare”不是一回事很多混淆来自这里。Go 里面有GORM PrepareStmt | v database/sql PrepareContext() | v pgx protocol Parse | v PostgreSQL Prepared Statement而pgx QueryExecModeCacheStatement | v pgx 自动管理 Prepared Statement这是两条不同路径。二、方案 A PrepareContext 会发生什么方案 A目标不要使用 prepared statement 避免 PgBouncer transaction 问题配置GORM: PrepareStmtfalse pgx: QueryExecModeExec PgBouncer: max_prepared_statements0但是你的代码ctx:context.Background()stmt,err:db.DB().PrepareContext(ctx,select * from users where id$1,)rows,err:stmt.QueryContext(ctx,1,)流程database/sql PrepareContext() | v pgx Parse statement name xxx | v PostgreSQL 创建 prepared statement此时QueryExecModeExec已经没有决定权。因为你已经告诉 driver请帮我 Prepare 一个 statement。所以方案 A 变成GORM | database/sql PrepareContext | pgx | Prepared Statement | PgBouncer transaction结果max_prepared_statements0 ↓ PgBouncer 不维护 prepared mapping ↓ 切换 backend ↓ stmt 不存在可能再次出现ERROR 26000 prepared statement xxx does not exist三、方案 B PrepareContext 会怎样方案 Bpgx QueryExecModeCacheStatement PgBouncer max_prepared_statements500但是你的代码stmt,err:db.PrepareContext(...)流程database/sql PrepareContext ↓ pgx 创建一个 SQL Prepared Statement ↓ PgBouncer这里不是pgx statement cache而是database/sql statement所以实际上变成GORM | database/sql stmt cache | pgx prepared statement | PgBouncer mapping你的链路多了一层。类似GORM PrepareStmt database/sql PrepareContext pgx statement PgBouncer prepared mapping非常复杂。四、那么 PrepareContext 有没有办法在 transaction 模式使用有但是需要满足条件。条件1PgBouncerpool_modesession最简单。因为client | backend 一直绑定Prepared Statement 生命周期一致。条件2transaction 模式必须max_prepared_statements 0并且你的 driver 行为必须符合 PgBouncer 支持范围。例如pool_modetransaction max_prepared_statements500然后客户端Parse named statement Bind ExecutePgBouncer 才能代理。五、那 GORM 的 PrepareStmt 呢例如db,_:gorm.Open(postgres.New(...),gorm.Config{PrepareStmt:true,},)它内部做的事情类似stmt,err:conn.PrepareContext(ctx,sql,)所以它等价于你手工PrepareContext()因此对于 PgBouncer transaction我的建议推荐PrepareStmt:false六、如果你希望同时使用 PrepareContext有没有推荐方案看目标。目标1稳定不要PrepareContext()改db.QueryContext(ctx,sql,args...)例如rows,err:db.QueryContext(ctx,select * from users where id$1,1,)让 pgx 决定Exec模式目标2极致性能使用PrepareContext()那么建议PgBouncer session pool或者直接PG connection pool例如pgxpool七、重新修正之前两个方案严格来说我前面给你的方案 A/B 默认前提是应用不显式调用 PrepareContext也不启用 GORM PrepareStmt。完整定义应该是方案A方案BGORM PrepareStmtfalsefalse手工 PrepareContext❌ 不允许⚠️ 可以但增加风险pgx CacheStatement❌✅PgBouncer max_prepared_statements0500transaction pool稳定性能更高八、结合你们项目GORM PgBouncer transaction你前面提到Go 程序大量使用 PrepareContext ExecContext这个信息非常关键。如果真实情况是stmt,err:db.PrepareContext(...)stmt.ExecContext(...)那么你们的问题根源很可能不是pgx QueryExecModeCacheStatement而是database/sql Prepared Statement PgBouncer transaction pooling这种情况下即使max_prepared_statements500也不一定能解决。因为你使用的是显式 prepared statement 生命周期而不是pgx 自动 statement cache 生命周期。这两个在 PgBouncer transaction 模式下是完全不同的问题。你们之前遇到的stmt xxx does not exist如果代码里大量存在PrepareContext/ExecContext这个方向的优先级应该放到最高。Go 程序大量使用 PrepareContext ExecContext 为了能不改应用代码 也不会报错 是不是只能使用 pgdog 连接池工具了Go 程序大量使用 PrepareContext ExecContext 为了能不改应用代码 也不会报错 是不是只能使用 pgdog 连接池工具了

相关新闻

197.集成光电检测、TON 定时推杆、多状态联锁停机的 S7-1200 完整自动化分拣工程

197.集成光电检测、TON 定时推杆、多状态联锁停机的 S7-1200 完整自动化分拣工程

摘要 可编程逻辑控制器(PLC)是工业自动化领域的核心控制设备。本文从工程实践出发,摒弃传统梯形图的入门方式,直接采用IEC 61131-3标准中的结构化文本(ST)语言,系统讲解PLC的核心原理、扫描周期机制、数据类型与寻址方式。通过一个完整的物料分拣控制案例,提供可直接运…

2026/10/13 7:58:06 阅读更多 →
GRBL-Plotter完全指南:如何用免费软件掌控你的CNC雕刻机

GRBL-Plotter完全指南:如何用免费软件掌控你的CNC雕刻机

GRBL-Plotter完全指南:如何用免费软件掌控你的CNC雕刻机 【免费下载链接】GRBL-Plotter A GCode sender (not only for lasers or plotters) for up to two GRBL controller. SVG, DXF, HPGL import. 6 axis DRO. 项目地址: https://gitcode.com/gh_mirrors/gr/G…

2026/10/13 7:58:07 阅读更多 →
195.具备自锁启停、模拟量线性转换、TOF 故障复位、CTU 启动计数、脉冲报警灯功能的完整电机控制 SCL 工程

195.具备自锁启停、模拟量线性转换、TOF 故障复位、CTU 启动计数、脉冲报警灯功能的完整电机控制 SCL 工程

摘要 可编程逻辑控制器(PLC)是工业自动化领域的核心控制设备。本文以西门子S7-1200系列PLC为硬件基础,TIA Portal V17为开发环境,系统讲解PLC的硬件架构、扫描周期原理、梯形图与结构化文本(SCL)编程方法。通过一个完整的电机启停控制与故障报警系统案例,提供可直接导入…

2026/10/3 23:32:45 阅读更多 →

最新新闻

Litellm:大模型调用的统一抽象层与生产级路由网关

Litellm:大模型调用的统一抽象层与生产级路由网关

1. 为什么“litellm”突然在开发者圈里刷屏?它到底不是个新模型,而是一把万能钥匙最近两周,好几个不同技术栈的同行在群里发截图:有人在用 Python 调通了本地运行的 Qwen3,有人把公司私有部署的 DeepSeek-R1 接进了旧版…

2026/10/13 7:57:23 阅读更多 →
agent-skills技能库实战:从工具调用到Agent能力层设计

agent-skills技能库实战:从工具调用到Agent能力层设计

做AI Agent应用开发这段时间,我踩得最深的坑,就是Agent表面上的能力边界很大,一旦落到具体场景里,连"查个数据再算个结果"这种基础操作都经常翻车。后来我把思路从"拼命塞提示词"转向"给Agent一套可复用…

2026/10/13 7:57:23 阅读更多 →
需求追踪工具reqtrace:从RTM缺口到可验证的覆盖矩阵

需求追踪工具reqtrace:从RTM缺口到可验证的覆盖矩阵

简介:reqtrace 是一款使用 Rust 语言开发的需求追踪工具,定位服务于软件研发团队、需求分析人员,以及关注研发工具链的开发者。它主要解决持续交付场景下如何低成本地解析和追踪各类需求工件的问题,整体设计强调可扩展、支持多种格…

2026/10/13 7:57:23 阅读更多 →
ArtCraft:工程化创意生产方法论与模块化AI工作流

ArtCraft:工程化创意生产方法论与模块化AI工作流

1. 项目概述:ArtCraft 不是“艺术手作”的简单拼接,而是一套可落地的创意生产方法论ArtCraft 这个词乍看像英文单词组合,但实际在当前内容创作与数字工具实践圈里,它已悄然演变为一个具象化的工作流代号——不是某个具体软件、平台…

2026/10/13 7:57:23 阅读更多 →
频谱分析仪选型实战:N9020A/N9020B/N9030A对比与避坑指南

频谱分析仪选型实战:N9020A/N9020B/N9030A对比与避坑指南

第一次见到N9020A,是在某研发团队的实验室里。当时我盯着那一排同族产品——N9020A、N9020B、N9030A,旁边还挂着安捷伦和是德科技两个logo,说实话一下子没分清谁是谁。后来自己上手用过、选过型、也看过不少二手仪器,才把这条产品…

2026/10/13 7:57:23 阅读更多 →
Ubuntu下ORB-SLAM2编译安装全攻略:依赖解析与避坑指南

Ubuntu下ORB-SLAM2编译安装全攻略:依赖解析与避坑指南

1. 为什么 ORB-SLAM2 的编译安装值得单独写一篇ORB-SLAM2 是视觉 SLAM 领域里被引用最多、被复现最多的开源系统之一,支持单目、双目和 RGB-D 三种传感器模式,能做实时定位与稀疏建图。很多人第一次接触它,都是被论文里的轨迹图和回环效果吸引…

2026/10/13 7:56:22 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →