数据库调整和可能的意外结果

时间:2014-03-20 22:51:19

标签: java database performance indexing

所以我的数据库工作速度比我想象的要慢一些,我想描述我正在做的事情,希望我可以验证我不会错过一些性能调整。首先是一些细节:

  • 我使用java并且数据库是H2嵌入式的(虽然无论数据库如何,我都能看到类似的结果)。

  • 我已经对它进行了分析/定时,问题肯定在数据库查询中,特别是executeQuery()调用。

  • 对于此示例,数据库表有60K行,但我的查询涉及25K行的子集,但需要整整1/2秒才能返回。

以下是我如何设置数据库表:

CREATE TABLE rows (
  id @AUTOTYPE@ PRIMARY KEY,

  x VARCHAR(255) NOT NULL,
  y VARCHAR(255) NOT NULL,
  z @TIMETYPE@ NOT NULL
)

CREATE INDEX rows_index ON rows(x, y, z);

(请注意,参数化分别用于自动增量和时间戳)

我的查询如下:

SELECT * FROM rows WHERE x = ? AND y = ? ORDER BY z DESC

现在,我只关心第一行,但它需要是最新的行,所以在java中看起来像这样:

PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, "foo");
stmt.setString(2, "bar");
stmt.setMaxRows(1);

注意最后一行的setMaxRows()设置为1.重申一下,整个'行' table有大约60K行,如果我没有将它设置为仅返回1,则查询将返回25K行。

我在做我能做的一切吗?即使1 / 2s(0.5s)肯定不是很长时间,但是看起来60K记录看起来不应该花那么长时间,特别是当它应该被索引时。我知道时间是相对的,所以这只是一个现代化的工作站,而不是在某种负载均衡的服务器上运行。

思考?谢谢你看看。

编辑:使用我尝试的内容进行更新:

  • 我尝试添加" LIMIT 1"在查询结束时,限制此数据库中结果数量的方式,并没有产生任何影响。实际上,数据库本身(H2)要么使用LIMIT,要么使用setMaxRows(),在我的结果中它们执行相同的操作。

  • 我尝试对我搜索的特定列进行索引,即创建3个索引语句,但同样没有产生性能差异。

2 个答案:

答案 0 :(得分:1)

理论上,你的

SELECT * FROM rows WHERE x = ? AND y = ? ORDER BY z DESC

需要收集所有这些行并对它们进行排序。一个智能数据库可以看透并发现它等同于

SELECT * FROM rows WHERE x = ? AND y = ? ORDER BY x, y, z DESC

并使用索引进行排序。你的数据库是否聪明?

更新:您也可以尝试

SELECT * FROM rows WHERE x = ? AND y = ? AND z = (SELECT MAX(z) FROM rows  WHERE x = ? AND y = ?)

如果您的数据库足够智能,可以有效地处理嵌套查询。您不应该首先提取MAX(z)并在单独的查询中使用它,但您可能需要尝试进行比较。


您正在获取单行,但数据库在开始处理时是否知道它?我会去TOP 1LIMIT 1,或者不管它是什么。

答案 1 :(得分:1)

您的基本问题是您的索引不是非常有选择性:X上的谓词和Y选择表中大约一半的行。这意味着您的执行计划可能如下所示:

  1. 扫描索引以查找满足XY谓词的行,并返回一组匹配的rowid。
  2. 对于步骤#1中的每个rowid,检索相应的表格行。
  3. 按降序对这些行进行排序。
  4. 将所有行返回给客户端。
  5. 由于setMaxRows(1)
  6. ,数据库驱动程序将丢弃除第一行之外的所有行

    这是一个相当愚蠢的计划,但它是基线。 H2可能会选择使用索引中找到的时间戳对步骤1返回的rowid进行排序,但我不希望这样。您可以使用H2提供的EXPLAIN PLAN命令找到实际计划(您应该对它非常熟悉)。

    但是,我们应该比该计划做得更好。如果您知道总是想要最新的记录,那么更改索引就足够了:

    CREATE INDEX rows_index ON rows(x, y, z DESC);
    

    这将以已排序的顺序返回步骤#1中的rowid集。数据库可能仍会返回所有这些行,但您不会每次都为这种行付费。

    这也可能会在select子句中使用top 1:索引扫描(步骤#1)可以在X和{{1}上进行第一次匹配后立即停止}。

    如果这不起作用,那么相关的子查询可能会起作用。我只是输入它,没有运行它,所以它可能包含拼写错误或语法错误,但它看起来像这样(你也应该能够对两个表进行别名并直接将内部查询与外部相关联,而不是重复参数):

    Y

    正如我所说,select * from ROWS where X = ? and Y = ? and Z = ( select max(Z) from ROWS where X = ? and Y = ? ) 是你的朋友。尝试几个查询,看看数据库正在做什么,然后尝试推动它做一些不同的事情。当我阅读H2性能优化指南时,它看起来似乎很聪明的查询优化。