加入收藏 | 设为首页 | 会员中心 | 我要投稿 沧州站长网 (https://www.0317zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

层次查询SQL性能故障不断?给你份可靠的避坑指南!

发布时间:2019-06-07 05:37:09 所属栏目:MySql教程 来源:蒋健
导读:近期频频遇到层次查询SQL的性能问题,结合历史故障案例,汇总了一些场景connect by常见的性能故障类型,在本文中做个分享。 一、结果中过滤or生成树中过滤 过滤条件放置于where后,为在结果树生成完成后裁剪叶子节点;放置于connect by后,为在生成树的过程

但其实这两种写法在语义上差别很大,结果集也可能不相同,如下:

  1.   SQL> select id from test2 where id3 = 10 start with id = 3 connect by nocycle prior id2 = id; 
  2.  
  3.     ID 
  4. ---------- 
  5.      8 
  6.  
  7. Elapsed: 00:00:00.13 
  8.  
  9. SQL> select id from test2  start with id = 3 connect by nocycle prior id2 = id and id3=10; 
  10.  
  11.     ID 
  12. ---------- 
  13.      3 
  14.  
  15. Elapsed: 00:00:00.00 

二、CBO估算不准确

层次查询的SQL语句频繁出现的问题,就是CBO估算返回结果集偏差,引起执行计划不准确。虽然表上收集过统计信息,但是CBO对于结果集的估算跟实际值偏差非常大(几百上千的倍的差距),但是这个也不能全怪CBO,毕竟递归查询有多少层、有多少数据要裁剪,结合起来考虑,结果确实难以估量。

层次查询SQL性能故障不断?给你份可靠的避坑指南!

层次查询SQL性能故障不断?给你份可靠的避坑指南!

对于CBO估算不准的问题,我们考虑了对结果集相对特殊的参数,在SQL文本上做区分,应用识别特殊参数运行带hint地改造SQL,通过hint来指定返回结果集。这种情况不同于普通的数据倾斜,无法通过baseline给出一个不涉及应用改造的方案。

层次查询SQL性能故障不断?给你份可靠的避坑指南!

三、并行处理

层次查询的SQL直接使用parallel的hint,会遭遇并行串行化的问题,也就是不能真正并行。对于一些重要且耗时长的层次查询,可以考虑PIPELINED TABLE FUNCTION改写SQL的方式来实现。

以下脚本测试参考了陈焕生童鞋的blog以及oracle相关文档(Doc ID 2168864.1):

  1. drop table t1; 
  2. -- t1 with 100,000 rows 
  3. create table t1 
  4. as 
  5. select 
  6.     rownum                      id, 
  7.     lpad(rownum, 10, '0')       v1, 
  8.     trunc((rownum - 1)/100)     n1, 
  9.     rpad(rownum, 100)           padding 
  10. from 
  11.     dual 
  12. connect by level <= 100000 
  13. ; 
  14.  
  15. begin 
  16.     dbms_stats.gather_table_stats(user,'T1'); 
  17. end; 
  18. / 
  19.  
  20. select /*+ monitor */ 
  21.     count(*) 
  22. from 
  23. ( 
  24.     select 
  25.         CONNECT_BY_ROOT ltrim(id) root_id, 
  26.         CONNECT_BY_ISLEAF is_leaf, 
  27.         level as t1_level, 
  28.         a.v1 
  29.     from t1 a 
  30.     start with a.id <=1000 
  31.     connect by NOCYCLE id = prior id + 1000 
  32. ); 
  33.  
  34. create or replace package refcur_pkg 
  35. AS 
  36.     TYPE R_REC IS RECORD (row_id ROWID); 
  37.     TYPE refcur_t IS REF CURSOR RETURN R_REC; 
  38. END; 
  39. / 
  40.  
  41. create or replace package connect_by_parallel 
  42. as 
  43.    /*  Naviagates a shallow hiearchy in parallel, where we do a tree walk for each root */ 
  44.  
  45.     CURSOR C1 (p_rowid ROWID) IS     -- Cursor done for each subtree. This select is provided by the customer 
  46.     select  CONNECT_BY_ROOT ltrim(id) root_id, CONNECT_BY_ISLEAF is_leaf, level as t1_level, a.v1 
  47.           from t1 a 
  48.           start with rowid = p_rowid 
  49.           connect by NOCYCLE id = prior id + 1000; 
  50.  
  51.     TYPE T1_TAB is TABLE OF C1%ROWTYPE; 
  52.  
  53.     FUNCTION treeWalk (p_ref refcur_pkg.refcur_t) RETURN T1_TAB 
  54.              PIPELINED 
  55.     PARALLEL_ENABLE(PARTITION p_ref BY ANY); 
  56.  
  57. END connect_by_parallel; 
  58. / 
  59.  
  60. create or replace package body connect_by_parallel 
  61. as  
  62. FUNCTION treeWalk (p_ref refcur_pkg.refcur_t) RETURN T1_TAB 
  63.           PIPELINED PARALLEL_ENABLE(PARTITION p_ref BY ANY) 
  64. IS 
  65.   in_rec p_ref%ROWTYPE; 
  66. BEGIN 
  67.    execute immediate 'alter session set "_old_connect_by_enabled"=true'; 
  68.    LOOP -- for each root 
  69.     FETCH p_ref INTO in_rec; 
  70.     EXIT WHEN p_ref%NOTFOUND; 
  71.     FOR c1rec IN c1(in_rec.row_id)  LOOP -- retrieve rows of subtree 
  72.         PIPE ROW(c1rec); 
  73.     END LOOP; 
  74.   END LOOP; 
  75.   execute immediate 'alter session set "_old_connect_by_enabled"=false';  
  76.   RETURN; 
  77. END  treeWalk; 
  78.  
  79. END connect_by_parallel; 
  80. / 
  81.  
  82. SELECT 
  83.   /*+ monitor */ 
  84.   COUNT(*) 
  85. FROM TABLE(connect_by_parallel.treeWalk (CURSOR 
  86.   (SELECT /*+ parallel (a 100) */ 
  87.     rowid FROM t1 a WHERE id <= 100))) b; 

(编辑:沧州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读