【问题标题】:Slow Informix COUNT/GROUP BY query, even with appropriate index缓慢的 Informix COUNT/GROUP BY 查询,即使有适当的索引
【发布时间】:2016-03-08 11:29:45
【问题描述】:

我有一个非常简单的查询,它在 Informix 11 中运行缓慢,即使存在适当的索引并且正在使用它:

select COUNTRY, COUNT(*) from EVENTS group by COUNTRY

有什么理由让它运行缓慢吗?我有使用 SQL Server 进行类似查询的经验,如果存在适当的索引,它们会立即执行。

更多信息:

  • 查询 EVENTS 表中的 500.000 条记录大约需要 15 秒(这让我很担心,因为该表将有数百万条记录,而且我发现执行时间正在迅速增加)。
  • EVENTS 表具有按 COUNTRY 的索引。通过使用 EXPLAIN 指令,我检查了该索引是否被使用。
  • EVENTS 表有很多列(大约 70 列)。
  • “国家”列是 varchar(32)。
  • “国家/地区”有 25 个不同的值。
  • 表扫描由 Informix 完成:

1)informix.EVENTS:索引路径 (1) Index Name: informix.country_ix Index Keys: COUNTRY (Serial, fragments: ALL) Query statistics: ----------------- Table map : ---------------------------- Internal name Table name ---------------------------- t1 EVENTS type table rows_prod est_rows rows_scan time est_cost ------------------------------------------------------------------- scan t1 501906 39285 501906 00:14.88 29390 type rows_prod est_rows rows_cons time est_cost ------------------------------------------------------------ group 25 4 501906 00:15.58 79761

【问题讨论】:

  • 您使用的是 11.10、11.50 还是 11.70 版本?您是否运行过 UPDATE STATISTICS?与使用最新版本(例如 12.10)相比,不运行它曾经是一个更大的问题,但它仍然值得检查。你在什么平台上运行?事件表中的一行有多大?
  • 它仍然需要读取所有索引页面才能计算每个国家/地区的事件数。
  • 我的 Informix 版本是 11.70.UC4D,在 Linux (Ubuntu) 中运行。我已经更新了统计数据,但没有任何区别。但是(正如我在下面告诉 Lorenzo)删除索引(并强制 SEQUENTIAL SCAN),查询执行得更快,这让我感到惊讶。我将尝试计算行大小。作为第一个近似值,它可能是每行大约 1kB。

标签: sql database performance optimization informix


【解决方案1】:

所以,我做了一些测试。

TL;DR

  • 将国家列类型更改为CHAR(32),重建索引,您应该会获得更好的性能。

加长版:

在 linux centos 7 上使用了 informix 12.10FC6DE(在 virtualbox 中创建的 VM)。用于 dbspace 的页面大小为 2048 字节,缓冲池为 50000 页。

创建了一个表 (tst),其行大小约为 425 字节(平均每页 4 行),包含几列。在这些列中,一个是country VARCHAR(32),另一个是static_country CHAR(32)。 用 499999 行填充表格,countrystatic_country 列均匀分布在 25 个国家/地区名称中。

创建了 2 个索引,一个在列 country (idx1_tst) 上,另一个在列 static_country (idx2_tst) 上。

表分区使用了 125000 个数据页(使用 oncheck -pT)。 索引使用了大约 1500 个页面(使用 oncheck -pT)。

A. 多次运行查询,强制进行顺序扫描(运行时间在 10 到 15 秒之间):

SELECT --+ FULL (tst)
    country, COUNT(*)
FROM
    tst
GROUP BY
    country

DIRECTIVES FOLLOWED:
FULL ( tst )
DIRECTIVES NOT FOLLOWED:

Estimated Cost: 1415645
Estimated # of Rows Returned: 25
Temporary Files Required For: Group By

  1) mydb.tst: SEQUENTIAL SCAN


Query statistics:
-----------------

  Table map :
  ----------------------------
  Internal name     Table name
  ----------------------------
  t1                tst

  type     table  rows_prod  est_rows  rows_scan  time       est_cost
  -------------------------------------------------------------------
  scan     t1     499999     499999    499999     00:12.17   140001

  type     rows_prod  est_rows  rows_cons  time       est_cost
  ------------------------------------------------------------
  group    25         25        499999     00:13.01   1275644

B. 多次运行查询,强制对 VARCHAR(32) 类型的 country 列索引进行 INDEX SCAN(运行时间在 4m30s 和 5m 之间):

SELECT --+ INDEX (tst idx1_tst)
    country, COUNT(*)
FROM
    tst
GROUP BY
    country

DIRECTIVES FOLLOWED:
INDEX ( tst idx1_tst )
DIRECTIVES NOT FOLLOWED:

Estimated Cost: 3462411
Estimated # of Rows Returned: 25

  1) mydb.tst: INDEX PATH

    (1) Index Name: mydb.idx1_tst
        Index Keys: country   (Serial, fragments: ALL)


Query statistics:
-----------------

  Table map :
  ----------------------------
  Internal name     Table name
  ----------------------------
  t1                tst

  type     table  rows_prod  est_rows  rows_scan  time       est_cost
  -------------------------------------------------------------------
  scan     t1     499999     499999    499999     04:49.71   3462411

  type     rows_prod  est_rows  rows_cons  time       est_cost
  ------------------------------------------------------------
  group    25         25        499999     04:50.51   1275644

C. 多次运行查询,强制对 CHAR(32) 类型的 static_country 列索引进行 INDEX SCAN(运行时间在 2 到 3 秒之间):

SELECT --+ INDEX (tst idx2_tst)
    static_country, COUNT(*)
FROM
    tst
GROUP BY
    static_country

DIRECTIVES FOLLOWED:
INDEX ( tst idx2_tst )
DIRECTIVES NOT FOLLOWED:

Estimated Cost: 16428
Estimated # of Rows Returned: 25

  1) mydb.tst: INDEX PATH

    (1) Index Name: mydb.idx2_tst
        Index Keys: static_country   (Key-Only)  (Serial, fragments: ALL)


Query statistics:
-----------------

  Table map :
  ----------------------------
  Internal name     Table name
  ----------------------------
  t1                tst

  type     table  rows_prod  est_rows  rows_scan  time       est_cost
  -------------------------------------------------------------------
  scan     t1     499999     499999    499999     00:02.02   16429

  type     rows_prod  est_rows  rows_cons  time       est_cost
  ------------------------------------------------------------
  group    25         25        499999     00:02.72   1277132

使用 sysmaster 数据库上的 SMI 表 sysptprof 我可以看到以下计数器(在运行之间使用 onstat -z 来重置计数器):

  1. 在情况 A(序列扫描)中:
    • 表 tst 分区:
      • lockreqs 499999
      • isreads 125001
      • bufreads 500060
      • pagreads 117532
  2. 如果是 B(VARCHAR 类型列上的 INDEX SCAN):
    • 表 tst 分区:
      • lockreqs 499999
      • isreads 499990
      • bufreads 999997
      • pagreads 348585
    • 索引 idx1_tst 分区:
      • lockreqs 499999
      • isreads 500009
      • bufreads 506961
      • pagreads 2545
  3. 如果是 C(CHAR 类型列上的 INDEX SCAN):
    • 索引 idx2_tst 分区:
      • lockreqs 499999
      • isreads 500000
      • bufreads 502879
      • pagreads 1440

所以,对于 SEQUENCIAL SCAN,正如我所料,只有表分区上有活动。

对于 CHAR 列上的 INDEX SCAN,只有索引分区上的活动,正如我所料(解释包含 Key-Only 指示)。

对于 VARCHAR 列上的 INDEX SCAN,表和索引分区中都有活动,这不是我所期望的(但正如费尔南多指出的那样,解释不包含 Key-Only 指示)。

我无法从 informix 中解释这种行为。但是一位同事向我指出了informix性能手册(版本12.10FC6,第10章,查询计划,访问计划)上的这个条目:

重要提示:优化器不会为 VARCHAR 选择仅键扫描 柱子。如果您想利用仅键扫描,请使用 ALTER 带有 MODIFY 子句的 TABLE 将列更改为 CHAR 数据类型。

【讨论】:

  • 非常感谢 Luis 的彻底调查和详细解释。我从 VARCHAR 更改为 CHAR,现在查询执行速度很快(250,000 条记录为 400 毫秒,500,000 条记录为 500 毫秒,1,000,000 条记录为 800 毫秒)。现在我的 sqexplain 说 Key-Only。将 COUNT(*) 替换为 COUNT(1) 没有任何区别。
【解决方案2】:

我会尝试的事情:

  • COUNT(1) 而不是 COUNT(*) 以防 DBMS 愚蠢
  • 在没有索引的情况下测试查询并检查执行计划,因为这可能会造成混淆
  • 测试哪些查询索引会加快速度并尝试不同的索引类型

【讨论】:

  • 感谢您的提示,洛伦佐。使用 COUNT(country)COUNT(1) 没有区别。但是,删除索引(Informix 使用该索引从 INDEX PATH 更改为 SEQUENTIAL SCAN)查询的执行速度要快得多(5 秒而不是 15 秒)。我想知道为什么!我想不出任何其他索引(与我的索引不同:单列“国家”)。
  • 看看其他选项的查询计划会很有趣。今天我在一个类似的查询中看到了这个“eefect”。值得注意的是,查询计划没有提到“仅键”,这与答案完美匹配,并且已验证正在访问数据分区。使用 COUNT(1) 应该可以消除这种影响。
猜你喜欢
  • 2021-12-19
  • 1970-01-01
  • 2013-05-30
  • 2020-08-15
  • 1970-01-01
  • 2017-06-29
  • 2015-05-19
  • 2021-09-17
  • 1970-01-01
相关资源
最近更新 更多