【问题标题】:Dynamic query with HibernateCritera API & Oracle - performance使用 Hibernate Criteria API 和 Oracle 进行动态查询 - 性能
【发布时间】:2010-12-15 20:36:19
【问题描述】:

我必须使用 Hibernate 并从 Oracle 检索数据,但问题是传递给查询的参数数量并不总是相同。

为简单起见,让我们考虑以下查询:

从 TAB_1 中选择 COL_1、COL_2、...、COL_N,其中 COL_1 在 (?, ?, ... ?) 中

传递给 in 子句的参数数量在 1 到 500 之间。如果数量在 1 到 50 左右,它的工作速度非常快,但是对于 200,它需要几秒钟来执行查询(解析、创建解释计划、执行查询)。索引已创建和使用 - 已检查。

查询是动态创建的,所以我使用 Hibernate Criteria API。对于第一个查询(具有 > 100 个参数),它需要 3-5 秒,但对于下一个查询,它的工作速度更快(即使参数数量不同)。我想改进第一个查询的响应时间。假设必须使用 Hibernate,在这种情况下我该怎么办?

我想删除这个动态查询,在 xml 文件中创建一些静态查询作为命名查询(在这种情况下,这些查询将在开始时预编译)例如

1) 参数个数少于50个时查询一次。

在这种情况下,如果我们有 30 个参数,查询将如下所示:

从 TAB_1 中选择 COL_1、COL_2、...、COL_N,其中 COL_1 在 (PAR_1, PAR_2, ..., PAR_30, -1, -1 , ..., -1 ?)

2) 如果数字在 50 到 100 之间,则为第二个。

问题在于使用命名查询和 HQL 并不是那么简单(在 JDBC 中它会很简单)。在 HQL 中,我们只传递了一个列表,并且我们没有在该列表中指定多个参数,即实际上只有一个查询

'from Person where id in (:person_list)'

myQuery.setParameterList("person_list", myList)

有没有办法解决这个问题?

顺便说一句,我认为解释计划是针对每个新查询执行的,例如:

(a) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) - 必须创建解释计划

(b) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) - 解释计划不会被创建,因为它已经存在于缓存中

(c) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) - 应该创建解释计划(查询没有解释计划有 120 个参数),但与 (a) 相比,它花费的时间更少,与 (b) 几乎相同,因此如果之前执行类似的查询,Oracle 可能可以更快地创建此计划

这是什么原因?

【问题讨论】:

  • select COL_1, COL_2, ..., COL_N from TAB_1 其中 COL_1 in (?, ?, ..., ?) - 是多个参数

标签: performance oracle hibernate dynamicquery


【解决方案1】:

这里有几件事。首先,你不能绑定一个 IN 列表,至少我很确定你不能。我怀疑 Hibernate 正在使用某种技巧,您将数组内容放入 Oracle 可以使用的静态 inlist 中。

其次,如果这个查询使用很多不同的参数执行,你必须绑定变量,否则整个数据库的性能会受到影响。

也就是说,有一种方法可以使用 Tom Kyte 在他的博客中描述的“技巧”来绑定 IN 列表 -

http://tkyte.blogspot.com/2006/01/how-can-i.html

里面的代码是这样的:

ops$tkyte@ORA10GR2> with bound_inlist
2  as
3  (
4  select
5    substr(txt,
6           instr (txt, ',', 1, level  ) + 1,
7           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
8           as token
9    from (select ','||:txt||',' txt from dual)
10  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
11  )
12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 30-JUN-05
OPS$TKYTE                             104 20-JAN-06

部分:

12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

基本上是您的查询所在。上面的位是将逗号分隔的字符串拆分为值列表的技巧。无需将列表绑定到 :txt 占位符,您需要将列表转换为字符串并绑定它。

您确定查询时间的差异不是由于机器上的缓存或负载变化造成的吗?解析查询需要一点时间,但几秒钟是很长的时间。

【讨论】:

  • 我很确定 oracle 没有缓存结果集。我运行了相同的查询,但参数不同,传递的参数列表包含不同的数字,而且速度非常快。我重复了几次。创建静态查询的另一种方法是使用查询: select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 = ?或 COL_1 = ?或 ... 或 COL_1 = ?
【解决方案2】:

我使用过IN(...) 查询,该列表中的 id 多达 1000 个;我可以向您保证,解析/准备/缓存语句不需要几秒钟。

Hibernate 确实会使用您传递的列表中的实际元素数量自动扩展您指定的参数列表,因此如果您真的想将其“固定”在某个级别,您需要做的就是追加足够的-1s 到最后。但是,这肯定不是问题,尤其是因为我们正在讨论加快 first 查询运行 - 反正还没有准备/缓存任何语句。

您是否查看了查询的执行计划?通过解释计划和自动跟踪都启用了吗?当您的列表中有 30 个元素和 120 个元素时,它们会有所不同吗?您的 actual 查询真的看起来像您发布的“从表中选择 ... from id in (...)”还是更复杂?我敢打赌,在 30 到 120 个元素之间,Oracle 决定(可能是错误地)不使用索引会更快,这就是您看到时间增加的原因。

【讨论】:

  • 实际上我的查询有一个额外的连接,但一般来说并不复杂。我已经在开发环境中检查了解释计划:1)在包含 2 或 3 个参数的子句中,在这种情况下,索引按预期使用 2)在包含 10 个或更多参数的子句中,并且有一个完整的扫描而不是索引。这令人惊讶,但事实证明该表包含数百条记录,并且与索引相比,全扫描更快(我们使用提示来比较成本 - 索引比全扫描慢,这很好)。
  • 我猜根据您的经验,使用一个连接、适当索引和少于 200 个 id 的动态查询应该持续更短(假设即使在将来这个表可能包含大约 5000 万条记录)?静态查询和动态查询有什么区别? 20%?
  • 另一个表是否加入了索引?您是否返回了 很多 数据?有几百条记录的表永远不是瓶颈。就静态查询和动态查询而言,差异取决于您的硬件、查询、oracle 配置和许多其他因素。不过,通常很多不到 20%。
猜你喜欢
  • 2019-02-05
  • 1970-01-01
  • 2012-01-14
  • 2011-05-14
  • 1970-01-01
  • 2012-06-03
  • 1970-01-01
  • 2011-01-31
  • 2010-12-30
相关资源
最近更新 更多