PreparedStatement Cache使用分析
关于数据库部分的SQL预编译缓存,立足于oracle数据库分析
PreparedStatement Cache两个部分
- 数据库服务端的PreparedStatement 缓存
- 客户端的PreparedStatement缓存
当前讲的是客户端PreparedStatement缓存
使用PreparedStatement的好处,参考此篇
PreparedStatement解读_wliang578的博客-CSDN博客
纠正问题
看到网上的文章,一次sql请求,PreparedStatement比Statement可以减少请求次数
根本没有这回事,请求数据库的次数都是一样的。创建PreparedStatement对象,在客户端创建预编译对象而已,真正请求到数据库是执行的时候。
用wireshark抓包,请求次数是一样的,但用上连接池pscache的比没用上,请求的数据会减少
这里是以orache数据库为例子,tns协议是oracle的自定义。
从图片看来,有pscache的发送给数据库的请求数据减少
无pscache

有pscache
数据库连接池对PreparedStatement Cache的实现
目前很多的数据库链接池都有实现dbcp,druid等,主流的数据库连接池都有实现。
优缺点
优点
- 减少临时创建PreparedStatement对象(这基本不减少耗时)
- 不归还PreparedStatement,也就是打开游标,不关闭游标。(可以提高性能)
缺点
- 增大项目的内存,引起fullgc(使用内存=链接数*最大的缓存数量*PreparedStatement的大小)
druid对减少这块内存有做优化,如果是用pscache可以用这个数据库链接池。
不适用场景
1 项目分库很多不适用(pscache是跟链接关联的),需考虑内存的情况
2 项目中占位符编写sql不多情况,缓存会用不上。
3 mysql数据库不要用,mysql的服务端sql的缓存是跟数据库连接关联的,没意义。
注意
- 数据库连接池中的PreparedStatement的缓存对象是跟着数据库链接绑定的
- 用pscache先要对项目进行分析,是否有这些不使用场景
- 在设置缓存的数量时,可以开启druid的sql监控,看下热sql的使用数量,做设置
- 关于pscache的命中情况,druid有监控,可以看
代码分析
基于druid连接池分析
创建prepareStatement对象
com.alibaba.druid.pool.DruidPooledConnection#prepareStatement(java.lang.String)

关闭prepareStatement
只是软关闭,将DruidPooledPreparedStatement对象的状态设置为关闭。而不是实际的关闭
com.alibaba.druid.pool.DruidPooledPreparedStatement#close
com.alibaba.druid.pool.DruidPooledConnection#closePoolableStatement

mysql为什么不开启pscache缓存
mysql数据库下不建议开启pscache,下面有详细的论述。
理由
- MySQL服务端SQL Cache也是Connection级别
用pscache

-
Class.forName("com.mysql.jdbc.Driver");
-
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/test?useServerPrepStmts=true&cachePrepStmts=true", "root", "root");
-
String prepareSql = "select * from user_info where firstName = ?";
-
PreparedStatement preparedStatement = conn.prepareStatement(prepareSql);
-
-
preparedStatement.setString(1, "zhuke");
-
preparedStatement.execute();
-
preparedStatement.close();
-
-
preparedStatement = conn.prepareStatement(prepareSql);
-
preparedStatement.setString(1, "zhuke1");
-
preparedStatement.execute();
-
preparedStatement.close();
使用mysql的预编译对象PrepateStatement时,一定需要设置useServerPrepStmts=true开启服务器预编译功能,设置cachePrepStmts=true开启客户端对预编译对象的缓存。
假设不开启(useServerPrepStmts=false)。 使用com.mysql.jdbc.PreparedStatement进行本地SQL拼装, 最后送到db上就是值已经替换了后的终于SQL
引用:JDBC使用PrepareStatement对性能的提升分析 - 简书
mysql预处理和非预处理性能对比
相关文章参考
JDBC之PreparedStatement的理解 - 灰信网(软件开发博客聚合)
JDBC preparedStatement和连接池PreparedStatementsCache_花飘万家雪的博客-CSDN博客
Druid源码阅读6-PreparedStatementPool源码及使用场景分析 - 掘金
PreparedStatement Cache使用分析_cacheprepstmts-CSDN博客
PreparedStatement解读_preparedstatement占位-CSDN博客
PreparedStatement预编译SQL,参数采用占位符的方式
为什么使用PreparedStatement
- 防止SQL注入风险
- 批处理,减少对数据库请求,减少程序耗时
PreparedStatement与Statement
两种方式,请求数据库的次数是相同。
Statement在数据库中属于一次编译使用(不使用占位符,不同的sql查询参数值,在数据库层面就是不同的SQL执行,不利用数据库层面的SQL缓存)
SQL耗时简单分析
SQL的耗时只要是在以下方面
- 解析SQL,生产执行计划
- 取数据
在解析SQL,生产计划,Statement属于一次性的使用。这款的耗时相比是增加的,在批处理的时候更能体现出来。
PreparedStatement多条语句,一次请求,可以用上相同的SQL执行计划,请求的次数减少,耗时也好。
PreparedStatement Cache的性能耗时对比_pscache-CSDN博客
PreparedStatement Cache的分析
PreparedStatement Cache使用分析_wliang578的博客-CSDN博客
性能压测基于oracle数据库的11.2的版本

优化后:增加了pscache。
压测场景:
1 两条简单的查询语句
orm框架:hibernate
数据库连接池:druid
缓存最大的pscache数量:20
从上述的对比来看,增加了pscache耗时还有增加,出乎意料。
从各大线程池(dbcp,druid)提供方,都说增加pscache对性能提升巨大,所以才去尝试。为什么结果会如此的不一样的
猜想
1 对低版本的数据库,可能是有用的(没做测试)。oracle服务端本身是有缓存,对于高频率的语句,也不会很快淘汰。特别是对于oracle这样的付费数据,注重性能,对这块本身会不断优化。
此片文章,写于2012年,都过去10年了,技术日新月异,是否还有用?
2 本人压测的场景有问题
综合自己分析,自认为是没问题的,单一的应用,单一的语句。
关于增加pscache耗时增加分析
这块耗时增加也是少量的,压测的量是很大的tps上百,需要的数据库连接会增加,缓存ps对象是要内存的,这块主要是新生代内存回收的时间
总结:通过压测分析,认为目前pscache对于oracle的一些版本,根本没减少耗时。所以建议不开启pscahe
如果有不同的答案,认为测试不合理,结果存疑,欢迎进行指正。

https://github.com/alibaba/druid/wiki/Oracle%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8BPreparedStatementCache%E5%86%85%E5%AD%98%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88
https://blog.csdn.net/xieyuooo/article/details/99877168
https://www.yisu.com/zixun/374687.html
浙公网安备 33010602011771号