【发布时间】:2016-01-09 00:26:27
【问题描述】:
我调查了很多地方,听到了很多可疑的说法,从 PreparedStatement 应该优先于 Statement 在任何地方,即使只是为了性能优势;一直声称PreparedStatements 应该专门用于批处理语句而不是其他任何东西。
但是,我所关注的(主要是在线)讨论似乎存在盲点。让我介绍一个具体的场景。
我们有一个带有数据库连接池的 EDA 设计应用程序。事件来了,有些需要坚持,有些则不需要。有些是人为生成的(例如,每 X 分钟更新/重置一次)。 一些事件按顺序出现和处理,但其他类型的事件(也需要持久性)可以(并且将)同时处理。
除了那些人为生成的事件之外,需要持久性的事件如何到达没有任何结构。
这个应用程序是很久以前(大约 2005 年)设计的,并且支持多个 DBMS。典型的事件处理程序(需要持久性):
- 从池中获取连接
- 准备sql语句
- 执行准备好的语句
- 处理结果集,如果适用,关闭它
- 关闭准备好的语句
- 如有必要,准备不同的语句并以相同的方式处理
- 将连接返回到池
如果一个事件需要批处理,则语句准备一次并使用addBatch/executeBatch 方法。这是一个明显的性能优势,这些案例与这个问题无关。
最近,我收到一个意见,即准备(解析)语句、执行一次并关闭的整个想法本质上是对 PreparedStatement 的滥用,无论是服务器还是服务器,都提供零性能优势使用客户端准备好的语句,并且典型的 DBMS(Oracle、DB2、MSSQL、MySQL、Derby 等)甚至不会将此类语句提升到准备好的语句缓存(或者至少,它们的默认 JDBC 驱动程序/数据源不会)。
此外,我必须在 MySQL 的开发环境中测试某些场景,Connector/J 使用分析器似乎同意这个想法。对于所有非批处理的预处理语句,调用close() 打印:
PreparedStatement created, but used 1 or fewer times. It is more efficient to prepare statements once, and re-use them many times
由于前面概述的应用程序设计选择,拥有一个 PreparedStatement 实例缓存来保存连接池中每个连接的任何事件使用的每条 SQL 语句听起来是一个糟糕的选择。
有人可以详细说明一下吗? “准备-执行(一次)-关闭”的逻辑是否存在缺陷并且基本上不鼓励?
附:为 Connector/J 显式指定 useUsageAdvisor=true 和 cachePrepStmts=true 并使用 useServerPrepStmts=true 或 useServerPrepStmts=false 仍然会导致在对 PreparedStatement 实例调用 close() 时为 every批处理 SQL 语句。
【问题讨论】:
-
传递给
PreparedStatement的参数,是从哪里来的?很多时候,PreparedStatement用于避免基于用户输入构建语句,这可能会破坏语句的结构(例如 SQL 注入)。 -
是的,我知道自动输入清理。主要是(整数)PK 随事件到达;有时原始输入(二进制数据字段,然后根据字段类型解析并使用
setXxx插入),否则setXxxs 可以使用全局或会话(上述顺序事件)变量调用,[单方面] 由应用程序。尽管如此,问题是是否不鼓励单执行然后关闭逻辑,而不管可能的副作用。 -
这完全取决于数据库系统和驱动程序。话虽如此,大多数驱动程序(和池)即使在您关闭它们时也会进行语句缓存。因此,除非您有一个长时间运行的处理线程(actor 样式),否则您希望遵循关闭 PS 的模式(因为当您返回连接时,池无论如何都会这样做)。保持 PS 打开的最大问题是,它们仅适用于当前连接,因此您还需要保持连接。