天天看點

sql查詢未走索引問題分析之查詢資料量過大

前因:

客戶咨詢,有一個業務sql(代表經常被執行且重要),全表掃描在系統占用資源很高(通過ash報告查詢得到資訊)      

思路:

1.找到sql_text,sql_id

2.檢視執行計劃

3.查詢sql涉及對象的對象資料量,段大小,行數量,where條件列,是否存在索引,列的選擇讀情況如何

4.總結,優化整改

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;      
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)

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或者根據應用需求,将此表進行拆分多個小表,這樣即使是全表掃描,相對來說量級别減少,查詢時間可能會提升,但是資源消耗并未降低(邏輯讀等消耗);