MySQL驱动预编译缓存需同时启用cachePrepStmts=true和useServerPrepStmts=true才生效,prepStmtCacheSize建议200~500,prepStmtCacheSqlLimit需适配SQL长度;HikariCP的statementCacheSize仅对非预编译语句有效,Oracle则需implicitCachingEnabled与statementCacheSize配合使用。
MySQL Connector/J 的 cachePrepStmts 和 prepStmtCacheSize 怎么配才有效
光开 cacheprepstmts=true 没用,缓存默认只存 25 条,且不启用服务端预编译(useserverprepstmts=false),实际走的还是客户端模拟预编译,软解析照常发生。
-
cachePrepStmts=true是开关,但必须配合useServerPrepStmts=true才能真正复用服务端的PreparedStatement对象 -
prepStmtCacheSize建议设为 200~500(默认 25),太小容易频繁淘汰;太大可能浪费内存,尤其当 SQL 变体多(如带不同 ID 的 WHERE 条件)时,缓存命中率反而下降 -
prepStmtCacheSqlLimit控制缓存 SQL 长度上限(默认 256),超长 SQL 直接不进缓存——动态拼接的复杂查询容易被漏掉 - 连接池(如 HikariCP)本身也有语句缓存(
cachePrepStmts是驱动层行为),二者不冲突,但驱动层缓存更底层、更直接
HikariCP 连接池里要不要额外开 statementCacheSize
HikariCP 的 statementCacheSize 是独立于 JDBC 驱动的缓存层,它缓存的是 Statement / PreparedStatement 实例对象,绕过驱动的 prepare 流程,对重复执行的相同 SQL(尤其是 Statement 类型)有明显加速效果。
- 仅当应用大量使用
createStatement()(非预编译)执行固定 SQL 时,这个配置才有价值;若全用prepareStatement(),优先靠驱动层缓存 - 值设为 50~100 即可,过高会导致连接对象膨胀,GC 压力上升
- 注意:该缓存不跨连接,每个连接独享一份缓存,所以总缓存大小 = 连接数 ×
statementCacheSize - 开启后需确保 SQL 字符串完全一致(包括空格、换行、参数占位符位置),否则视为不同语句,无法命中
Oracle JDBC 的 implicitCachingEnabled 和 statementCacheSize 必须成对用
Oracle 驱动的隐式语句缓存(implicit cache)和 MySQL 的机制完全不同:它依赖服务端的「游标共享」能力,且缓存的是已解析并执行过的 PreparedStatement 句柄,不是 SQL 文本。
- 必须同时设置
implicitCachingEnabled=true和statementCacheSize=XX(默认 10,建议 50~200),单独开一个无效 - 缓存键是
SQL + 参数类型 + fetch size + resultSetType等组合,只要其中任一变化(比如从executeQuery()改成executeUpdate()),就无法复用 - Oracle 12c+ 推荐搭配
oracle.jdbc.autoCommitSpecCompliant=false,避免 autocommit 切换导致缓存失效 - 错误现象:
ORA-01000: maximum open cursors exceeded很可能是缓存 size 设得过大,而应用未及时close()Statement 导致句柄泄漏
缓存失效的三个隐蔽原因
即使参数全配对、SQL 完全一致,缓存也可能静默失效,排查时容易忽略底层行为。
- 调用了
setFetchSize()、setResultSetType()等设置方法后才执行,会触发新缓存条目创建(MySQL 驱动中,这些属性影响缓存 key) - 连接被归还到池中时,某些池实现(如 older DBCP)会强制
clearBatch()或重置语句状态,导致缓存条目被丢弃 - MySQL 驱动在自动重连(
autoReconnect=true)后,旧缓存全部清空——这个行为不会报错,但性能陡降,需关掉重连或改用故障转移机制
缓存不是开个开关就完事,关键是确认它真在命中的路径上工作。最简单的验证方式:打开驱动日志(logger=com.mysql.cj.logger.StandardLogger + profileSQL=true),看 prepare 调用是否变少,以及是否出现 “Cached statement” 提示。










