2016-03-08 109 views
1

我有一个Informix中的11即使适当的索引存在运行速度慢一个非常简单的查询,它正在使用:慢的Informix COUNT/GROUP BY查询,即使有合适的索引

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

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

的更多信息:

  • 查询花费500.000记录15秒左右的活动表(让我担心,因为这个表将有上百万的记录,我已经看到,执行时间正在迅速增加)。
  • EVENTS表按国家/地区列出索引。通过使用EXPLAIN指令,我检查了这个索引正在被使用。
  • EVENTS表有许多列(约70)。
  • “country”列是varchar(32)。
  • “国家”有25个不同的值。
  • 表扫描是通过Informix的完成:

 
1) informix.EVENTS: INDEX PATH

(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

+0

您使用的是11.10,11.50还是11.70版本?你有没有运行UPDATE STATISTICS?没有运行的问题比过去的版本(例如12.10)要多得多,但仍然值得检查。你在哪个平台上运行?事件表中的一行有多大? –

+0

它仍然需要阅读所有索引页面才能计算每个COUNTRY的事件数量。 –

+0

我的Informix版本是在Linux(Ubuntu)中运行的11.70.UC4D。我更新了统计数据,但没有任何区别。然而(正如我在下面告诉洛伦佐)删除索引(并强制连续扫描),查询执行得更快,这让我感到惊讶。我会尝试计算行大小。作为第一个近似值,它可能是每行大约1kB。 –

回答

2

所以,我做了一些测试。

TL; DR

  • 改国号列类型CHAR(32),重建索引,你应该有更好的性能。

长的版本:

二手的Informix 12.10FC6DE在Linux CentOS的7(VM在VirtualBox中创建)。用于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.运行查询多次,迫使序列完备SCAN(运行时间分别为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.运行查询多次,迫使索引扫描在country列索引,它的类型的VARCHAR(32)(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.运行查询几次,迫使一个索引扫描的static_country列索引,它的类型CHAR(32)的(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(序列完备SCAN):
    • 表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

因此,对于序列完备SCAN只有对表分区活动,如我所料。

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

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

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

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

+0

非常感谢你的彻底调查和你的详细解释,路易斯。我从VARCHAR更改为CHAR,现在查询执行得很快(250,000条记录为400毫秒,500,000为500毫秒,1,000,000为800毫秒)。现在我的sqexplain说只有钥匙。用COUNT(1)替换COUNT(*)没有区别。 –

1

事我会尝试:

  • COUNT(1)而不是COUNT(*)的情况下,数据库管理系统是愚蠢的
  • 测试查询和检查没有索引的执行计划,因为我牛逼可能是混乱
  • 源检测一下了查询索引速度和尝试不同的索引类型
+0

谢谢你的提示,洛伦佐。使用'COUNT(country)'或'COUNT(1)'没有区别。但是,除去索引(Informix从INDEX PATH更改为SEQUENTIAL SCAN),查询执行得更快(5秒而不是15)。我想知道为什么!我想不出任何其他指数(与我拥有的不同:单列“国家”)。 –

+0

查看其他选项的查询计划会很有趣。 今天我在类似的查询中看到了这个“eefect”。值得注意的是,查询计划没有提及“仅键”,并且这与完美匹配答案并验证数据分区正在被访问。 使用COUNT(1)应该消除这种影响。 –