《架构师》2017年4月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

二、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的了,时代在发展,有些以前适用的配置也许就不再合适了。