oracle代碼靜態性能掃描

oracle代碼靜態性能掃描

傳統性能分析

傳統的SQL性能分析手段,主要是針對oracle代碼的動態分析,也就是通過oracle後臺的各種性能視圖,捕獲執行過的SQL執行語句以及執行情況,包括執行時間、cost、等待事件等等,並以此作爲SQL上線前SQL審覈的依據。但是我們在測試過程中肯定會遇到以下這種情況,oracle的代碼沒有被完全測試覆蓋,部分代碼沒有被調用過。由於oracle存儲過程中的代碼與實際的測試案例不能做到有效的關聯,因此這種情況普遍存在,並且影響着投產後的數據庫性能穩定。

代碼靜態性能掃描

代碼靜態掃描的目的就是利用oracle獲取靜態執行計劃的方法,利用文本規則判斷的技術,實現批量方式的代碼靜態性能掃描

具體流程如下:

  1. 獲取PACKAGE BODY,FUNCTION,PROCEDURE的名字,可以從USER_OBJECT視圖中獲取,我是從ddl對象監控清單中獲取變化過的程序名字。
  2. 從USER_SOURCE視圖中拼出程序代碼。之前希望利用11g的LISTAGG函數拼,但是這個函數有長度限制,因此通過python腳本拼接。
  3. 從程序代碼中找出sql,這裏有個限制,動態拼接的sql是無法正常識別的,因此這裏只掃描正常的sql語句。由於程序中大都使用了變量作爲參數,而識別變量的規則相對複雜,因此這裏偷懶,利用ORACLE的報錯代碼ORA-00904:”XXX”: invalide identifier,識別每次執行報錯之後的XXX字段,並用:XXX替換成變量模式,通過不斷的try exception最終得到可以正常執行的sql
  4. 通過explain plan for的方式獲取執行計劃
  5. 利用python正則表達是做規則的分析,例子中只做了執行計劃和沒有where條件兩種規則,其他規則後續慢慢更新。

代碼

#coding=utf-8
import cx_Oracle
import re
import hashlib



def GetSqlPlan(conn,sql):
   res3=[]
   try:
       sqlhash = (str(len(sql))+hashlib.new("md5", sql.encode("utf-8")).hexdigest())[0:30]
       sql2 = "explain plan set statement_id='"+sqlhash+"' for " + sql
       cursor2 = conn.cursor()
       cursor2.execute(sql2.encode('utf-8'))
       sql3 = "select * from table(dbms_xplan.display('PLAN_TABLE','"+sqlhash+"','ALL'))"
       cursor2.execute(sql3)
       res3 = cursor2.fetchall()
       print (sqlhash,'\n',sql)
       for y in res3:
           print(y[0])
   except cx_Oracle.DatabaseError as msg:
       e,=msg.args
       oerr_code=e.code
       oerr_text =e.message
       if oerr_code ==904:
           oerr_col=re.match('ORA-(.*?): (.*?):.*',oerr_text).group(2).replace("\"","")
           #print (oerr_col)
           sql=sql.replace(oerr_col,":"+oerr_col)
           #print (sql)
           GetSqlPlan(conn,sql)
   return sql,res3



def SearchPackBody(conn,object_name,object_type):
   sql1 ="SELECT UPPER(TRIM(s.TEXT)) FROM user_source s  WHERE s.TYPE='"+object_type+"' AND s.NAME='"+object_name+"' ORDER BY S.name, S.line"
   cursor1 = conn.cursor()
   cursor1.execute(sql1)
   res1 = cursor1.fetchall()
   sqlbody=[]
   for x in res1:
       sqlbody.append(x[0])
   sqlbody="".join(sqlbody)
   sqltext=sqlbody.replace('\n',' ')
   sqllist=[]
   for startstr in ["SELECT","UPDATE","MERGE","DELETE","INSERT"]:
       sqltmp=re.findall(r' '+startstr+'(.*?);',sqltext)
       for x in sqltmp:
           sql=startstr+x
           sqllist.append(sql)
           #print("@@@@@@",object_name)
           a,b=GetSqlPlan(conn,sql)
           RuleCheck(a,b)
   return sqllist

def SearchAll():
   conn = cx_Oracle.connect('test/test#[email protected]/testdb')
   sql="SELECT OBJECT_NAME,OBJECT_TYPE FROM DBA_OBJECTS WHERE OBJECT_TYPE IN ( 'PACKAGE BODY','PROCEDURE','FUNCTION') AND OWNER='CIIS'"
   cursor = conn.cursor()
   cursor.execute(sql)
   res = cursor.fetchall()
   for y in res:
       SearchPackBody(conn,y[0],y[1])

def RuleCheck(sql,plan):
   #沒有where條件
   if  (re.search('FROM',sql) and not re.search('WHERE',sql)):
       print("沒有WHERE條件語句",sql)
   #執行計劃掃描
   for x in plan :
       if (re.search('TABLE ACCESS FULL',x[0])):
           print("全表掃描",x[0])
       if (re.search('NESTLOOP', x[0])):
           print("NESTLOOP連接", x[0])



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