为什么DISTINCT对3个相同的值这么慢?

时间:2017-09-13 15:53:45

标签: oracle query-optimization profiler

我遇到了一个我无法理解的性能问题。我正在执行的查询如下:

SELECT UNIQUE GROUP_ID
  FROM pos, TABLE (t_type(12984918, 12984919, 12984917))
  WHERE pos.pos_id = COLUMN_VALUE AND GROUP_ID <> 0;

1 row returned in 99 ms 

你可能认为83毫秒没问题,但令我感到困惑的是,即使只有3个重复值,删除UNIQUE也会使查询更快:

SELECT GROUP_ID
  FROM pos, TABLE (t_type(12984918, 12984919, 12984917))
  WHERE pos.pos_id = COLUMN_VALUE AND GROUP_ID <> 0;

3 rows returned in 0.048 ms 

oracle真的需要花费近100毫秒来将一组3个元素减少为单个值吗?这对我来说似乎很疯狂,所以我开始做一些调查并用TKPROF获取执行计划。

SELECT UNIQUE GROUP_ID
  FROM pos, TABLE (t_type(12984918, 12984919, 12984917))
 WHERE pos.pos_id = COLUMN_VALUE AND GROUP_ID <> 0

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          2          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.09          0          8          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.10          0         10          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 58  
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  HASH UNIQUE (cr=8 pr=0 pw=0 time=99258 us cost=31 size=3892 card=139)
         3          3          3   NESTED LOOPS  (cr=8 pr=0 pw=0 time=84 us)
         3          3          3    NESTED LOOPS  (cr=5 pr=0 pw=0 time=59 us cost=30 size=3892 card=139)
         3          3          3     COLLECTION ITERATOR CONSTRUCTOR FETCH (cr=0 pr=0 pw=0 time=7 us cost=29 size=16336 card=8168)
         3          3          3     INDEX UNIQUE SCAN IU_POS_POS_ID (cr=5 pr=0 pw=0 time=31 us cost=0 size=0 card=1)(object id 20684)
         3          3          3    TABLE ACCESS BY INDEX ROWID POS (cr=3 pr=0 pw=0 time=12 us cost=0 size=26 card=1)

嗯,使用HASH UNIQUE操作,Oracle看起来真的需要99毫秒才能将3个相同的值减少到一个值。实际上,STILL似乎很疯狂。这几乎不应该引人注意。所以我开始寻找替代方案。事实证明,Oracle也可以使用SORT UNIQUE操作来实现DISTINCT。我还发现我可以通过执行以下操作强制oracle不使用HASH UNIQUE

ALTER SESSION SET "_gby_hash_aggregation_enabled" = true;

现在,我的查询运行时间为0.48毫秒!这是一个2000倍的改进! WTF!以下是使用SORT UNIQUE显示的执行计划:

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          4          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0         16          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        6      0.00       0.00          0         20          0           2

Misses in library cache during parse: 2
Optimizer mode: ALL_ROWS
Parsing user id: 58  
Number of plan statistics captured: 2

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT UNIQUE (cr=8 pr=0 pw=0 time=48 us cost=30 size=28 card=1)
         3          3          3   NESTED LOOPS  (cr=8 pr=0 pw=0 time=35 us)
         3          3          3    NESTED LOOPS  (cr=5 pr=0 pw=0 time=26 us cost=29 size=28 card=1)
         3          3          3     COLLECTION ITERATOR CONSTRUCTOR FETCH (cr=0 pr=0 pw=0 time=2 us cost=29 size=6 card=3)
         3          3          3     INDEX UNIQUE SCAN IU_POS_POS_ID (cr=5 pr=0 pw=0 time=17 us cost=0 size=0 card=1)(object id 20684)
         3          3          3    TABLE ACCESS BY INDEX ROWID POS (cr=3 pr=0 pw=0 time=7 us cost=0 size=26 card=1)

********************************************************************************

好的,现在我正在思考如何在不触及会话参数的情况下强制oracle使用SORT UNIQUE。事实证明我可以添加一个ORDER BY子句,并加快我的查询速度。

SELECT UNIQUE GROUP_ID
  FROM pos, TABLE (t_type(12984918, 12984919, 12984917))
 WHERE pos.pos_id = COLUMN_VALUE AND GROUP_ID <> 0
 ORDER BY GROUP_ID;

所以基本上我已经解决了我的问题(如果你可以称之为那种黑客&#34;解决问题&#34;),但我仍然感到困惑。

以下是问题:

  • 为什么Oracle选择HASH UNIQUE而不是SORT UNIQUE
  • 当只有3条哈希记录时,为什么HASH UNIQUE这么慢?
  • 有没有更好的方法来暗示oracle使用SORT UNIQUE而不是HASH UNIQUE

2 个答案:

答案 0 :(得分:2)

  

为什么Oracle选择HASH UNIQUE而非SORT UNIQUE?

您的第一个tkprof结果显示TABLE迭代器的基数高得多(预期?)。 Oracle正在针对超过三行进行优化。

  

当只有3条记录要哈希时,为什么HASH UNIQUE这么慢?

Hash unique将需要构建一个哈希表,无论有多少元素被哈希化。据推测,对于较少的元素,哈希表的大小会更小,但基数估计值也是如此,以至于Oracle将构建更大的哈希表。

  

有没有更好的方法来暗示oracle在HASH UNIQUE上使用SORT UNIQUE?

我会尝试在表格操作符上暗示基数。

答案 1 :(得分:0)

  

有没有更好的方法来暗示oracle在HASH上使用SORT UNIQUE   UNIQUE?

我会检查如何收集表和索引统计信息。它们由oracle优化器使用,以便构建有效的访问计划。