PreparedStatement未生效的三大原因:驱动未开启预编译(如MySQL需useServerPrepStmts=true)、SQL含动态拼接、参数类型不匹配;MySQL需合理配置useServerPrepStmts与cachePrepStmts;PostgreSQL依赖prepareThreshold阈值触发预编译。

PreparedStatement 为什么没生效?检查这三处
很多情况下你以为用了 PreparedStatement 就自动预编译+缓存了,其实 JDBC 驱动根本没走预编译流程——它悄悄退化成了普通 Statement。
常见错误现象:EXPLAIN 显示每次执行都重新解析 SQL;慢查询日志里出现大量重复的相同语句;数据库端观察到 prepare 调用极少甚至为零。
- 驱动未开启预编译支持:MySQL 的
useServerPrepStmts=true默认是false,PostgreSQL 的preferQueryMode=extended需显式设置 - SQL 字符串含动态拼接:比如
"SELECT * FROM t WHERE id = " + userId,哪怕包在PreparedStatement里也无效 - 参数类型不匹配导致驱动放弃缓存:例如用
setString(1, "123")给数字字段赋值,某些驱动会拒绝复用已编译计划
MySQL 下 useServerPrepStmts 和 cachePrepStmts 怎么配
这两个参数不是“开了就快”,配错反而拖慢连接初始化或浪费内存。
使用场景:高并发、固定 SQL 模板多(如分页查询、状态更新)、单次连接内重复执行同一 SQL 超过 3 次以上。
-
useServerPrepStmts=true:强制让 MySQL 服务端做预编译,否则只在客户端模拟(无缓存效果) -
cachePrepStmts=true:开启客户端 PreparedStatement 缓存,但仅当useServerPrepStmts=true时才真正有意义 -
prepStmtCacheSize=250:默认 25,小项目够用;大系统建议设到 100–500,避免频繁淘汰 -
prepStmtCacheSqlLimit=2048:太短会截断长 SQL 导致缓存失效,建议至少 4096
典型连接 URL:jdbc:mysql://localhost:3306/db?useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=4096
PostgreSQL 的 prepareThreshold 是什么鬼
PostgreSQL JDBC 驱动不用“预编译开关”,而是靠 prepareThreshold 控制何时升格为 prepared statement。
常见错误现象:明明写了 PreparedStatement,但 pg_stat_statements 里看不到 PREPARE 记录;首次执行极慢,后续却没变快。
-
prepareThreshold=1:每次执行都尝试准备(适合稳定 SQL),但连接首次开销略大 -
prepareThreshold=5(默认):前 4 次走简单协议,第 5 次才真正 prepare —— 如果你连接生命周期短于 5 次,永远不生效 -
prepareThreshold=0:禁用 prepare,全走简单协议(调试时可临时设这个看基线性能)
注意:若 SQL 含 IN (?) 且参数个数变化(如 IN (1) vs IN (1,2,3)),会被视为不同语句,无法共享计划。
缓存失效的隐蔽原因:不只是 SQL 文本不同
你以为改个空格、换行、注释就没事?错。JDBC 驱动对 SQL 归一化的程度很有限,很多看似微小的差异都会导致缓存 miss。
容易踩的坑:
- 大小写混用:
SELECT和select在部分驱动中算不同语句 - 列别名带引号:
SELECT name AS "user_name"和SELECT name AS user_name不等价 - WHERE 条件顺序变化:
WHERE a=1 AND b=2和WHERE b=2 AND a=1多数驱动不归一 - 数据库版本升级后计划缓存格式变更:比如 PostgreSQL 14 升 15,旧缓存直接丢弃
验证是否命中缓存最直接的方式:开启驱动日志(如 MySQL 加 logger=com.mysql.cj.log.StandardLogger&profileSQL=true),搜 Cache hit 或 Cache miss。
真正复杂的点不在怎么开,而在怎么确认它一直在工作——尤其在灰度发布、SQL 动态生成、ORM 拦截器介入的场景下,缓存可能悄无声息地失效很久。










