【数据库】Oracle单表查询性能优化 - 索引应用

目录

问题说明

解决方案 - 场景A

解决方案 - 场景B

解决方案 - 场景C

深入理解索引 - 索引的逻辑结构

深入理解索引 - 索引的三个特点

深入理解索引 - 索引的注意事项


 

问题说明

系统上线后,用户数据不断增长,从最初的几十万记录发展至上千万、甚至上亿条记录。运维人员反馈原有的存储过程运行效率愈发低下。review时发现以下可改进之处:

场景A:数据表的索引与查询语句子条件(where)筛选字段无关,未合理创建索引; 

场景B:业务逻辑分析不足,产生非必要的表关联,50%的语句可改写为单表查询语句;

场景C:数据表本身已有相关索引,但在查询时未正确使用索引

 


 

各场景解决方案:  由于不方便用项目代码举例,以下sql涉及的字段名、数据表名均用别名替换。

 

场景A:.必要表连接 + 无索引,可调整为 必要表连接 + 索引检索

表b:file_name,file_month,file_type(索引I:file_month)

表d: fee,file_name,file_month(无索引)

 

   SELECT fee

        FROM d

   WHERE file_name IN  (SELECT file_name FROM b WHERE file_month=202002 and file_type= 'XXX');

 

【执行计划 - 必要表连接 + 无索引】

                              

优化分析】由于业务层面上,B与D表的file_month不等价,需保留表连接。执行计划中的索引扫描“INDEX(FAST FULL SCAN)”用到了b表的索引I(file_month),可在 表d 的file_name列新建索引提升查询性能。

新建索引】CREATE INDEX d_file_name_idx ON d (file_name) ;

执行计划 - 必要表连接 + 索引检索

                         

成本优化】:269416 --  1087

 


场景B非必要表连接  +  无索引 ,可调整为 无表连接 + 索引检索:

表c:file_name, file_month

表d:fee,file_name,file_month(数据量 3018488)

 

     SELECT fee

              FROM d 

      WHERE file_name IN (SELECT file_name FROM c WHERE file_month=202002);

 

执行计划 - 非必要表连接  +  无索引 】:

                             

优化分析】d表的file_month与c表的file_month在业务层面是等价的,因此,c、d表无需关联,并在file_month列建索引:

【无表连接】 

                   SELECT fee   

                                    FROM d 

                                   WHERE file_month= 202002;

 

执行计划 - 无表连接  +  无索引

                             

 

【新建索引】       CREATE INDEX d_file_month_idx ON d (file_month) ;

执行计划 - 无表连接  +  索引检索
                             

成本优化】268758 - 267987 - 5

 


场景C:索引列嵌套函数,未合理使用索引

表d:amount, file_day(索引)

 

SELECT  dis_info_fee
              FROM d
              WHERE  ROUND(file_day/100)=202002;

【执行计划 - 索引列嵌套函数】

                       

优化分析】表d的索引列file_day也在使用中嵌套了函数,数据查询时无法使用索引,为避免在索引列使用函数,需以业务逻辑为基准改写查询方式。业务需求为查询当月的数据,故修改为:

SELECT dis_info_fee
             FROM d
             WHERE file_day >= 202002*100+1 AND file_day < (202002+1)*100 

执行计划 - 索引列无附加函数

                       

成本优化】268170 - 14930

 


深入理解索引:

1、索引的逻辑结构:

         

                                                   

索引的逻辑结构分为三部分:Root(根块)、Branch(茎块)、Leaf(叶子块)。

Leaf(叶子块)存储【 key column value(索引列具体值)+ 数据块所在rowid】,即索引块,当一个Leaf(叶子块)存满后,则开辟新空间,存储到新的Leaf(叶子快),这时出现了两个Leaf(叶子块)的管理者 ---- Branch(茎块)。

Branch(茎块)存储指向Leaf(叶子块)的指针,当一个Branch(茎块)存满后,则开辟新空间,存储到新的Branch(茎块),这时出现了两个Branch(茎块)的管理者 ----  Root(根块);

... ... ... ...

以此类推,索引的逻辑结构可一直往上堆叠,呈现“金字塔”结构。

由于索引块只包含个别字段的信息,而数据块包含数据表所有字段的信息,使用索引列查询可提高查询效率。

 

2、索引的三个特点:

a.索引高度较低:

b.索引存储列值 + ROWID值,且 索引列取值可为空

c.索引本身有序

 

以下示例仍以表d为例, 数据量1.3亿。表d:fee,cdr_seq(索引列)

 

2.1 特点a应用 :索引列 = 某一具体值                                                    

SELECT * 

             FROM d 

             WHERE cdr_seq = 'XXXXXXX';

【执行计划】

                                  

按“金字塔”结构存储索引,五百万记录,索引高度可能为 两 层;

                    500G数据包含几百亿条记录,索引高度仅为 六 层;

通过索引检索一条数据只需完成(高度 + 1)次I/O操作。

需注意:查询字段包括除索引列的字段时,会产生回表(TABLE ACCESS (BY INDEX ROWID)),增加 I/O开销。

 

2.2 特点b应用 --- 统计函数 COUNT( ) / SUM( ) / AVG( )

SELECT COUNT(*)

                 FROM  d  ---  1.3亿,58.84s

【执行计划】

                                

 

SELECT COUNT(*)

                FROM  d 

                WHERE  cdr_seq  IS NOT NULL ---  1.3亿,22.84s

【执行计划】

                               

【成本优化】1086226 - 166539

本示例说明索引列取值可为空,若指定索引字段取值非空,COUNT(*) / SUM(*) / AVG(*)函数可以通过索引字段统计信息,提高统计效率。

 

2.3 特点c应用 --- ORDER BY / DISTINCT 

SELECT  cdr_seq  

            FROM   d  

            WHERE cdr_seq  >= 1230201 ORDER BY cdr_seq  

【执行计划 - 无索引】

 

 【执行计划 - 索引检索】

本示例说明新建索引后,因索引的有序性,可消除ORDER BY语句产生的排序(SORT (ORDER BY))

 

2.4 特点b/c综合应用 --- 统计函数 MAX( ) / MIN( )

SELECT MAX(cdr_seq)

                      FROM d;

【执行计划】

                  

本示例并未限定索引列非空,在执行计划中仍然用到索引扫描,再次说明索引列可以取值为空。另一方面,索引本身是有序的,在读取最大值时,只需读取最右边叶子块的最后一条记录,即可获得最大值。

 

综上,总结四种索引扫描类型

  扫描类型 说明  
1 INDEX RANGE SCAN

局部扫描;                                返回记录越少,效率越少;

索引列 = 具体取值
2 INDEX FAST FULL SCAN

全局扫描,一次读取多个索引块;本身无序,可能需要额外排序;

COUNT(),SUM(),AVG()
3 INDEX FAST SCAN

全局扫描,一次读取一个索引块;本身有序,可消除排序;

ORDER BY,DISTINCT
4 INDEX  FULL SCAN(MIN/MAX)

局部扫描,效率最高

MAX()只需读取最右边的叶子快;MN()只需读取最左边的叶子快;

 

3、使用索引的注意事项:

          a. 分区局部索引:若筛选条件只有索引字段,用不到分区字段,性能将会下降;

          b. 可通过组合索引避免回表(TABLE ACCESS by INDEX ROWID)造成的性能下降;

          c. 聚合因子越低 (表和索引两者的排列顺序相似度越高),排序计算量越低;

          d. 分析执行计划时应关注成本(COST),必要时可用空间(BYTES)消耗换取成本(COST)优化;

          e. 不可在索引列嵌套函数;

          f. 根据业务场景精简表结构字段;

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章