MySQL JDBC 驱动程序将这两个属性定义为:
useServerPrepStmts - 如果服务器支持,是否使用服务器端准备好的语句?
cachePrepStmts - 如果驱动程序缓存客户端准备语句的PreparedStatements 的解析阶段,则“检查” 服务器端准备和服务器端准备的适用性 陈述本身?
客户端准备好的语句是重用
PreparedStatements
对象的方法吗?
如果启用了
useServerPrepStmts
,那么到底缓存了什么,因为 MySQL 无论如何都没有 执行计划缓存?
首先,区分客户端和服务器准备好的语句很重要。
客户端准备好的语句是“模拟”准备好的语句。这意味着 SQL 语句字符串在客户端被标记化,并且在将语句发送到服务器执行之前,所有占位符都被替换为文字值。每次执行时都会将完整的 SQL 语句发送到服务器。您可以使用常规日志来研究其工作原理。例如
以下代码:
ps=conn.prepareStatement("select ?")
ps.setInt(1, 42)
ps.executeQuery()
ps.setInt(1, 43)
ps.executeQuery()
会在日志中显示:
255 Query select 42
255 Query select 43
“查询”表示在协议级别发送
COM_QUERY
命令,后面带有语句字符串。
服务器准备好的语句是“真正的”准备好的语句,这意味着查询文本被发送到服务器,进行解析,并将占位符和结果信息返回到客户端。这就是设置
useServerPrepStmts=true
时得到的结果。语句字符串仅通过 COM_STMT_PREPARE
调用发送到服务器一次(记录于 here)。每次执行都是通过发送一个 COM_STMT_EXECUTE
来执行的,其中包含准备好的语句句柄和用于替换占位符的文字值。
为了与客户端准备的示例进行对比,我们可以使用类似的代码块(但这次启用了服务器准备的语句):
ps2=conn2.prepareStatement("select ?")
ps2.setInt(1, 42)
ps2.executeQuery()
ps2.setInt(1, 43)
ps2.executeQuery()
日志会显示:
254 Prepare select ?
254 Execute select 42
254 Execute select 43
您可以看到该语句在执行之前已准备好。日志对我们有帮助,并显示了执行的完整语句,但实际上,每次执行时仅将占位符值从客户端发送到服务器。
许多连接池会在连接的使用中缓存准备好的语句,这意味着如果您调用
conn.prepareStatement("select ?")
,它将在使用相同语句字符串的连续调用中返回相同的 PreparedStatement
实例。这对于避免在事务之间将连接返回到池中时在服务器上重复准备相同的字符串很有用。
MySQL JDBC 选项
cachePrepStmts
将以这种方式缓存准备好的语句(客户端和服务器准备好的语句),并缓存语句的“准备性”。 MySQL中有一些语句在服务器端是无法准备的。如果驱动程序认为可能的话,它将尝试在服务器上准备一条语句,如果准备失败,则返回到客户端准备好的语句。由于需要往返服务器,因此该检查的成本很高。该选项还将缓存此检查的结果。
希望这有帮助。
在 mariadb jdbc 连接器中,cachePrepStmts 选项 自 3.0.0 起已被删除