為什麼v$sql中的執行次數會被重置
這幾天一直在觀察生產環境中一個長時間執行的sql,我是用select * from v$sql where sql_id=*** 去觀察的。因為這個sql是遊標裡面的語句,通過它執行的次數,我可以判斷這個程式大概什麼時候可以執行完畢。就在隔了一天,我再次用這個語句觀察sql的執行次數時發現EXECUTIONS被重置了,數量比我前天觀察到的小很多(在v$sql中通過sqlid只有一行資料),但是這個程式還在執行!我當時觀察了 v$sql
的FIRST_LOAD_TIME,LAST_LOAD_TIME時間,前一個是sql開始執行的時間,這個沒問題,後一個時間是凌晨時間,也就是說,凌晨的時候這個sql被重新載入了!我當時沒想明白為什麼會重新載入,今天看了篇有關父遊標和子游標的文章後,才明白可能是sqlid在執行時,產生了子游標!!!!!
首先簡單說明下父遊標和子游標:
1. oracle是一個採用共享機制的資料庫,如果sql能夠共享,它是不會重新生成遊標的(包括父遊標和子游標);
2. 一個sql在首次被載入時,一定是產生一個父遊標和一個子遊標,V$SQL可以觀察子游標,v$sqlarea可以觀察父遊標
SELECT T.FIRST_LOAD_TIME, T.LAST_LOAD_TIME, T.LAST_ACTIVE_TIME, T.CHILD_NUMBER, T.LOADS, T.EXECUTIONS, T.CHILD_ADDRESS FROM V$SQL T WHERE T.SQL_ID = 'gvhnpfk1ytyh3';
SELECT T.SQL_ID,
T.EXECUTIONS,
T.LOADED_VERSIONS,
T.LOADS,
T.ADDRESS
FROM V$SQLAREA T
WHERE T.SQL_ID = 'gvhnpfk1ytyh3';
V$SQL中的FIRST_LOAD_TIME是表示父遊標的載入時間,LAST_LOAD_TIME表示對應子游標的載入時間,child_number表示子游標的編號(從0開始);
v$sqlarea中的EXECUTIONS是父遊標下面全部子游標總共的執行次數(其實我應該從這裡來觀察總的執行次數)LOADED_VERSIONS表示有幾個子游標。
所以通過V$SQL中的LAST_LOAD_TIME時間的變化,應該可以判斷出來確實產生了新的子游標,從而導致EXECUTIONS被重置(這個時候的EXECUTIONS是新的子游標執行的次數)。解決了EXECUTIONS被重置的問題後,又引出了2個問題:1.為什麼在V$SQL中我通過sqlid只看到一行記錄;2. 是什麼原因導致了正在執行的sql產生了新的子游標?
第一個問題,可能是因為前一個子遊標因為空閒被置換出了記憶體;
第二個問題,可以通過一個檢視來尋找答案:v$sql_shared_cursor
SELECT * FROM v$sql_shared_cursor t WHERE t.SQL_ID='gvhnpfk1ytyh3';
這個檢視記錄了每個子游標產生的原因(即sql不能共享的原因),通過幫助文件,我觀察到其中有個原因很可能就是這次sql產生子游標的原因:STATS_ROW_MISMATCH(The existing statistics do not match the existing child cursor)很可能是定時的表分析使統計資料發生了變化,從而導致了新子游標的產生。。。希望明天去公司能夠證實我的想法。。