二、MySQL是否真的不用打开PSCache?
一般在设置连接池时,都会有类似下面的设置:
<property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
很多文章上都说PSCache对使用游标的数据库有巨大的性能提升,但MySQL不建议开启,因为它不支持游标。所以很多人在用MySQL时,都会将poolPreparedStatements设置为false,就连Druid的文档上也是这么写的。
但事实真的是这样么,MySQL使用PSCache真的对性能没有提升么?
先来看看关于游标的问题,其实大部分文章的表述不太准确,现在的MySQL在存储过程里是支持游标的,但其他地方的确不支持,具体详见官方手册(MySQL supports cursors inside stored programs.)。但这并不是我们要讨论的关键。
3.1.0版本后的JDBC驱动里有一个参数是useServerPrepStmts,如果服务器支持的话,会开启服务端PreparedStatement,默认是false。官方手册中有如下说明:
Server-side Prepared Statements - Connector/J 3.1 will automatically detect and use server-side prepared statements when they are available (MySQL server version 4.1.0 and newer).
也就是说在MySQL 4.1.0版本后,3.1.0以上的驱动会检测到支持服务端PreparedStatement,并且启用该特性。根据MySQLTUTORIAL上的说明,整个过程分为PREPARE、EXECUTE和DEALLOCATE PREPARE三步。MySQL JDBC驱动的Contributor Jess Balint在StackOverflow上做了一个详细的说明,《High-Performance Java Persistence》的作者也专门撰写文章分析了两者的区别。
ps=conn.prepareStatement("select ? ") ps.setInt(1, 42) ps.executeQuery() ps.setInt(1, 43) ps.executeQuery()
上述代码在使用客户端PreparedStatement时,MySQL日志里看到的是:
255 Query select 42 255 Query select 43
如果用的是服务端PreparedStatement,看到的则是(实际每次执行只会传占位符的值,语句是不传的):
254 Prepare select ? 254 Execute select 42 254 Execute select 43
在整个使用过程中,Prepare只会做一次,在这时服务端会对语句进行解析,后续收到具体值时会优化执行计划。如果同一条语句每次都新建PreparedStatement,那么每次都会多一回网络交互和语句解析,这显然是可以优化的。
综上所述,现在在使用MySQL时(如果版本比较新的话),出于性能考虑,应该在数据库连接池上开启针对PreparedStatement的缓存。如果没有使用连接池,或者所用的连接池不支持PSCache,也可以在JDBC连接上设置cachePrepStmts=true。
事实上,MySQL的JDBC驱动还有不少针对性能的优化,比如设置useConfigs=maxPerformance(请酌情使用),相当于同时做了如下设置:
cachePrepStmts=true cacheCallableStmts=true cacheServerConfiguration=true useLocalSessionState=true elideSetAutoCommits=true alwaysSendSetIsolation=false enableQueryTimeouts=false
各位同学,是时候检视一下自己的系统是如何连接MySQL的了,时代在发展,有些以前适用的配置也许就不再合适了。