【DB筆試面試578】在Oracle中,什麼是會話游標?會話游標和共享游標的區別有哪些?會話游標分為哪幾類?
- 2019 年 10 月 10 日
- 筆記
♣
題目部分
在Oracle中,什麼是會話游標?會話游標和共享游標的區別有哪些?會話游標分為哪幾類?
♣
答案部分
(一)會話游標的含義
會話游標(Session Cursor)是當前會話(Session)解析和執行SQL的載體,即會話游標用於在當前會話中解析和執行SQL,會話游標是以哈希表的方式快取在PGA中(共享游標是快取在SGA的庫快取里)。在目標SQL的執行過程中,會話游標起承上啟下的作用。因為Oracle依靠會話游標來將目標SQL所涉及的數據從Buffer Cache的對應數據塊讀到PGA里,然後在PGA里做後續的排序、表連接等處理,最後將最終的處理結果返回給用戶,所以,會話游標是當前會話解析和執行SQL的載體。
共享游標和會話游標的對比如下表所示:
|
共享游標(Shared Cursor) |
會話游標(Session Cursor) |
---|---|---|
快取位置 |
快取在SGA中的共享池裡的庫快取(Library Cache)中。 |
快取在每個會話的PGA中。 |
共享 |
共享游標在所有會話之間共享。 |
會話游標(Session Cursor)與會話(Session)是一一對應的,不同會話的會話游標之間不能共享,這是與共享游標(Shared Cursor)的本質區別。 |
生命周期 |
共享游標無生命周期,會進行快取。 |
會話游標是有生命周期的,每個會話游標在使用的過程中都至少會經歷一次Open、Parse、Bind、Execute、Fetch和Close中的一個或多個階段。Oracle會根據參數SESSION_CACHED_CURSORS的值來決定是否將已經用過的會話游標快取在對應會話的PGA中。 |
聯繫 |
1.會話游標是以哈希表的方式快取在PGA中,意味著Oracle會通過相關的哈希運算來存儲和訪問在當前會話的PGA中的對應會話游標。這種訪問機制和共享游標是一樣的,可以簡單地認為Oracle是根據目標SQL的SQL文本的哈希值去PGA中的相應Hash Bucket中找匹配的會話游標。由於在快取會話游標的哈希表的對應Hash Bucket中,Oracle會存儲目標SQL對應的父游標的庫快取對象句柄地址,所以,Oracle可以通過會話游標找到對應的父游標,進而就可以找到對應子游標中目標SQL的解析樹和執行計劃,然後Oracle就可以重用目標SQL的解析樹和執行計劃來執行SQL語句了。2.一個會話游標只能對應一個共享游標,而一個共享游標卻可以同時對應多個會話游標。 |
(二)會話游標的分類
會話游標的詳細分類參考下表:
表 3-20 Oracle中會話游標的分類

在上表中需要注意的是,動態游標是Oracle資料庫中最靈活的一種會話游標,它的靈活性表現在:①動態游標的定義方式非常靈活,它可以有多種定義方式。②動態游標可以作為存儲過程的輸入參數和函數的輸出參數。上表中的各種游標希望讀者可以通過做大量的練習題來掌握,畢竟游標是存儲過程開發過程中必不可少的內容。
(三)會話游標的屬性
會話游標有4個屬性,見下表:
表 3-21 游標的屬性
屬性 |
類型 |
簡介 |
適用對象 |
適用SQL |
---|---|---|---|---|
SQL%FOUND |
布爾型 |
最近的FETCH是否提取到數據,表示一條SQL語句被執行成功後受其影響而改變的記錄數是否大於或等於1,若是則賦值為TRUE,否則為FALSE。在一條DML語句被執行前,SQL%FOUND的值是NULL。 |
隱式游標、顯式游標 |
INSERT、DELETE、UPDATE、SELECT … INTO … |
SQL%NOTFOUND |
布爾型 |
最近的FETCH是否沒有提取到數據,表示一條SQL語句被執行成功後受其影響而改變的記錄數是否為0,若是則賦值為TRUE,否則為FALSE。在一條DML語句被執行前,SQL%NOTFOUND的值是NULL。 |
隱式游標、顯式游標 |
|
SQL%ROWCOUNT |
數值型 |
表示最近的一條SQL語句成功執行後受其影響而改變的記錄的數量,後續執行的SQL會覆蓋SQL%ROWCOUNT的值。 |
隱式游標、顯式游標 |
|
SQL%ISOPEN |
布爾型 |
游標是否打開,當游標打開時返回TRUE。對於隱式游標而言,SQL%ISOPEN的值永遠是FALSE。 |
顯式游標 |
當執行一條DML語句後,DML語句的結果保存在這四個游標屬性中,這些屬性用於控制程式流程或者了解程式的狀態。當運行DML語句時,PL/SQL打開一個內建游標並處理結果。在這些屬性中,SQL%FOUND和SQL%NOTFOUND是布爾值,SQL%ROWCOUNT是整數值。需要注意的是,若游標屬於隱式游標,則在PL/SQL中可以直接使用上表中的屬性,若游標屬於顯式游標,則上表中的屬性里「SQL%」需要替換為自定義顯式游標的名稱。上表中的這4個屬性對於動態游標依然適用。
(四)會話游標的相關參數
和會話游標相關的有兩個重要參數,分別為OPEN_CURSORS和SESSION_CACHED_CURSORS,下面詳細介紹這兩個參數。
(1)參數OPEN_CURSORS用於設定單個會話中同時能夠以OPEN狀態並存的會話游標的總數,默認值為50。若該值為300,則表示單個會話中同時能夠以OPEN狀態並存的會話游標的總數不能超過300,否則Oracle會報錯「ORA-1000:maximum open cursors exceeded」。視圖V$OPEN_CURSOR可以用來查詢資料庫中狀態為OPEN或者己經被快取在PGA中的會話游標的數量和具體資訊(例如,SQL_ID和SQL文本等)。當然,也可以從視圖V$SYSSTAT中查到當前所有以OPEN狀態存在的會話游標的總數。
LHR@orclasm > show parameter open_cursors NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ open_cursors integer 65535 SELECT USERENV('SID') FROM DUAL; SELECT * FROM V$OPEN_CURSOR WHERE SID=16; SELECT * FROM V$SYSSTAT D WHERE D.NAME ='opened cursors current';
(2)參數SESSION_CACHED_CURSORS用於設定單個會話中能夠以Soft Closed狀態快取在PGA中的會話游標的總數。在Oracle 10g中默認為20(注意:在官方文檔中記錄的該值默認為0是有誤的),11g中默認為50。
LHR@orclasm > show parameter session_cached_cursors NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ session_cached_cursors integer 50
從上述顯示結果可以看出,SESSION_CACHED_CURSORS的值為50,意味著在這個庫里,單個會話中同時能夠以Soft Closed狀態快取在PGA中的會話游標的總數不能超過50。
關於參數SESSION_CACHED_CURSORS需要注意以下幾點:
① Oracle會用LRU演算法來管理這些已快取的會話游標(從會話游標的dump文件中可以證實這一點),所以即便某個Session以Soft Closed狀態快取在PGA中的會話游標的總數己經達到了SESSION_CACHED_CURSORS所設置的上限也沒有關係,LRU演算法依然能夠保證那些頻繁反覆執行的SQL所對應的會話游標的快取命中率要高於那些不頻繁反覆執行的SQL。
① 在Oracle 11gR2中,一個會話游標能夠被快取在PGA中的必要條件是該會話游標所對應的SQL解析和執行的次數要超過3次。Oracle這麼做的目的是為了避免那些執行次數很少的SQL所對應的會話游標也被快取在PGA里,這些SQL很可能只執行一次而且不會重複執行,所以把這些執行次數很少的SQL所對應的會話游標快取在PGA中是沒有太大意義的。可以使用如下的SQL語句查詢快取在當前系統中的所有會話游標:
SELECT D.INST_ID, D.SQL_ID,D.SQL_TEXT,D.SID,D.USER_NAME,D.HASH_VALUE FROM GV$OPEN_CURSOR D WHERE D.CURSOR_TYPE='SESSION CURSOR CACHED';
下面給出一個會話游標快取的示例:
LHR@orclasm > alter system flush shared_pool;--生產庫慎用 System altered. --開始第1次執行 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; no rows selected LHR@orclasm > SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP; COUNT(*) ---------- 14 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; no rows selected --開始第2次執行: LHR@orclasm > SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP; COUNT(*) ---------- 14 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; no rows selected --開始第3次執行: LHR@orclasm > SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP; COUNT(*) ---------- 14 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; SQL_ID CURSOR_TYPE ------------- ---------------------------------------------------------------- 9r01dt51f46tf DICTIONARY LOOKUP CURSOR CACHED 從結果可以看到,雖然已經快取到PGA中了,但是類型為「DICTIONARY LOOKUP CURSOR CACHED」,並不是「SESSION CURSOR CACHED」,所以下面開始第4次執行: LHR@orclasm > SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP; COUNT(*) ---------- 14 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; SQL_ID CURSOR_TYPE ------------- ---------------------------------------------------------------- 9r01dt51f46tf SESSION CURSOR CACHED LHR@orclasm > SELECT a.VERSION_COUNT,a.EXECUTIONS,a.PARSE_CALLS,a.LOADS FROM v$sqlarea a WHERE a.SQL_ID='9r01dt51f46tf'; VERSION_COUNT EXECUTIONS PARSE_CALLS LOADS ------------- ---------- ----------- ---------- 1 4 3 1 --從結果可以看到,在SQL語句「SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP;」第4次執行完畢後,Oracle已經將其對應的會話游標快取在當前會話的PGA中了,而此時快取的會話游標的類型為「SESSION CURSOR CACHED」。下面開始第5次執行: LHR@orclasm > SELECT /*test scc*/ COUNT(*) FROM SCOTT.EMP; COUNT(*) ---------- 14 LHR@orclasm > SELECT D.SQL_ID,D.CURSOR_TYPE FROM V$OPEN_CURSOR D WHERE D.SID=USERENV('SID') AND D.SQL_TEXT LIKE 'SELECT /*test scc*/ COUNT(*)%' ; SQL_ID CURSOR_TYPE ------------- ---------------------------------------------------------------- 9r01dt51f46tf SESSION CURSOR CACHED LHR@orclasm > SELECT a.VERSION_COUNT,a.EXECUTIONS,a.PARSE_CALLS,a.LOADS FROM v$sqlarea a WHERE a.SQL_ID='9r01dt51f46tf'; VERSION_COUNT EXECUTIONS PARSE_CALLS LOADS ------------- ---------- ----------- ---------- 1 5 3 1
從結果看出,快取的會話游標的類型依然為「SESSION CURSOR CACHED」,不再改變。
(五)會話游標的dump文件
會話游標的dump文件可以通過Level值為3的errorstack得到,獲取過程如下所示:
SELECT COUNT(*) FROM SCOTT.EMP;--執行5次,讓其快取在PGA中 ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME ERRORSTACK LEVEL 3'; SELECT COUNT(*) FROM SCOTT.EMP; ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME ERRORSTACK OFF'; SELECT VALUE FROM V$DIAG_INFO;
本文選自《Oracle程式設計師面試筆試寶典》,作者:李華榮。