• sql查询未走索引问题分析之查询数据量过大


    前因:

    客户咨询,有一个业务sql(代表经常被执行且重要),全表扫描在系统占用资源很高(通过ash报告查询得到信息)

    思路:

    1.找到sql_text,sql_id

    2.查看执行计划

    3.查询sql涉及对象的对象数据量,段大小,行数量,where条件列,是否存在索引,列的选择读情况如何

    4.总结,优化整改

    1.找到sql_text,sql_id

    094cmrxrahdy2
    SELECT 8~10个列名称(由于设计用户信息,因此部分信息不再详细说明)
    FROM Prescription 
    WHERE ProcFlg=0 AND(Group_No=0 OR Group_No=1 OR Group_No=99) AND MachineNo<>99 ORDER BY Presc_Class DESC, PrescriptionNo, SeqNo;

    2.查看执行计划

    SQL> select * from table(dbms_xplan.display_cursor('094cmrxrahdy2',format=>'IOSTATS LAST'));
    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------------
    Plan hash value: 1208083363
    ----------------------------------------------------
    | Id | Operation | Name | E-Rows |
    ----------------------------------------------------
    | 0 | SELECT STATEMENT | | |
    | 1 | SORT ORDER BY | | 335K|
    |* 2 | TABLE ACCESS FULL| PRESCRIPTION | 335K|
    ----------------------------------------------------
    PLAN_TABLE_OUTPUT
    -----------------------------------------------------
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    2 - filter(("PROCFLG"=0 AND INTERNAL_FUNCTION("GROUP_NO") AND "MACHINENO"<>99))
    INTERNAL_FUNCTION 内部函数,一般执行计划看到这个需要特殊关注,但是本次确认输入的数值类型等同于字段类型

    GROUP_NO NOT NULL NUMBER(2)

    3.查询sql涉及对象的对象数据量,段大小,行数量,where条件列,是否存在索引,列的选择读情况如何

    1)查询表所在的用户
    select owner,object_name,object_type,status from dba_objects where object_name='PRESCRIPTION' OWNER OBJECT_NAME OBJECT_TYPE STATUS -------------------- -------------------- ------------------- ------- PUBLIC PRESCRIPTION SYNONYM VALID PHARMACY PRESCRIPTION TABLE VALID
    2)查询表的段大小

    select sum(bytes)/1024/1024 from dba_segments where segment_name='PRESCRIPTION' and owner='PHARMACY';
    SUM(BYTES)/1024/1024
    --------------------
    450

    3)查询表上的索引,及索引对应列名称

    select index_owner,index_name,column_name,COLUMN_POSITION from dba_ind_columns where table_owner='PHARMACY' and table_name='PRESCRIPTION'

    INDEX_OWNER INDEX_NAME COLUMN_NAME COLUMN_POSITION
    ------------------------------ -------------------- -------------------- ---------------
    PHARMACY PRESCRIPTION_IDX1 PRESC_CLASS 1
    PHARMACY PRESCRIPTION_IDX1 PRESCRIPTIONNO 2
    PHARMACY PRESCRIPTION_IDX1 SEQNO 3
    PHARMACY PRESCRIPTION_IDX2 PROCFLG 1
    PHARMACY PRESCRIPTION_IDX3 PROCFLG 1
    PHARMACY PRESCRIPTION_IDX3 GROUP_NO 2
    PHARMACY PRESCRIPTION_IDX3 MACHINENO 3
    PHARMACY PK_PRESCRIPTION PRESCRIPTIONNO 1
    PHARMACY PK_PRESCRIPTION SEQNO 2

    9 rows selected.

    --第一,where 条件的三个列,再上述结果中,均存在对应的记录,所以基本可以排除无索引导致全表扫描的问题

    --第二,产生疑问,存在索引,为何未使用索引???  猜测数据倾斜严重,SQL查询数据量过大,统计信息不准确等信息导致的问题,需要进一步进行分析

    4)查询表行数量,及最后一次收集统计信息的时间

    SQL> select num_rows,last_analyzed  from dba_tables where owner='PHARMACY' and table_name='PRESCRIPTION';

    NUM_ROWS    LAST_ANALYZED
    -----------------------------
    1560341    2018-11-27 22:01:31

    5)查询where 条件列的选择性(及去重后的行数量)

    WHERE ProcFlg=0 AND(Group_No=0 OR Group_No=1 OR Group_No=99) AND MachineNo<>99 

    看起来最差的选择性<>条件MachineNo列

    查询发现,表总160万行,MachineNo列只有一个值1,也不存在Null值,where条件<>99,是无价值的条件,但是不至于影想走索引,此条件近乎无用

    SQL> select count(*),count(distinct MachineNo) from PHARMACY.PRESCRIPTION;
    COUNT(*) COUNT(DISTINCTMACHINENO)
    ---------- ------------------------
    1604912 1
    --
    SQL> select MachineNo,count(*) from PHARMACY.PRESCRIPTION group by MachineNo;
    MACHINENO COUNT(*)
    ---------- ----------
    1 1604912



    看起来选择性最好的条件ProcFlg=0, 符合这个条件的数值表中存在49万条记录,占表中记录的1/4,选择性已经很差了

    SQL> select count(*),count(distinct ProcFlg) from PHARMACY.PRESCRIPTION;
    COUNT(*) COUNT(DISTINCTPROCFLG)
    ---------- ----------------------
    1604912 4
    select ProcFlg,count(*) from PHARMACY.PRESCRIPTION group by ProcFlg;
    PROCFLG COUNT(*)
    ---------- ----------
    -1 7
    1 1110365
    2 995
    0 493545 


    看起来选择性中等的Group_No=0 OR Group_No=1 OR Group_No=99--符合条件的数值足有110万条记录

    select count(*),count(distinct Group_No) from PHARMACY.PRESCRIPTION;
    COUNT(*) COUNT(DISTINCTGROUP_NO)
    ---------- -----------------------
    1604912 2
    select Group_No,count(*) from PHARMACY.PRESCRIPTION group by Group_No;
    GROUP_NO COUNT(*)
    ---------- ----------
    1 1111367
    2 493545 

    --组合过滤后,只有4种可能性,对于本次sql,

    --条件ProcFlg=0 存在50万条记录
    --Group_No 1 or 0 or 99 返回110万行记录

    --全表 1604912 --160万行记录,提取记录110/160=68.5%数据量,执行效率过低,还不如走全表扫描

    SQL> select count(*),count(distinct ProcFlg||' '||Group_No) from PHARMACY.PRESCRIPTION;

    COUNT(*) COUNT(DISTINCTPROCFLG||''||GROUP_NO)
    ---------- ------------------------------------
    1604914 4

     4.总结,优化整改

    1)该SQL走全表扫描是正确的,全表扫描比回表查询65%全表数据量更快

    2)提高该SQL性能,无法从索引入手,因为SQL是查询10个列,且数据量过大,不适用索引快速检索数据

    3)建议开发人员,重新针对业务逻辑,规划新表:

    01对每个表建立主键约束(唯一值),让业务SQL能快速定位一个唯一的记录,通过索引,快速检索少量数据,减少资源的消耗(逻辑读等消耗);

    02或者根据应用需求,将此表进行拆分多个小表,这样即使是全表扫描,相对来说量级别减少,查询时间可能会提升,但是资源消耗并未降低(逻辑读等消耗);

  • 相关阅读:
    redis在centos7下安装(源码编译)
    Centos7之Gcc安装
    Jmeter工具之上传图片,上传音频文件接口
    什么是系统平均负载(Load average)
    sonar+Jenkins 构建代码质量自动化分析平台
    数据库主从相关配置参数说明
    现有数据库配置主从同步
    MySQL5.7多主一从(多源复制)同步配置
    MySQL5.7主从从配置
    MySQL5.7主从同步配置
  • 原文地址:https://www.cnblogs.com/lvcha001/p/10140016.html
Copyright © 2020-2023  润新知