1. 程式人生 > >對Oracle 語句快取設定和JDBC PreparedStatement的試驗

對Oracle 語句快取設定和JDBC PreparedStatement的試驗

筆者之前曾經寫過一篇Sybase ASE在JDBC -Statement和PreparedStatement兩種SQL執行方式下執行效率和監控指標的對比文章,本文沿用類似的方法,在Oracle 11gR2資料庫中繼續驗證兩種方式在SQL效率和資料庫指標上的差異。

一、      Oracle SQL執行過程

借用《Pro OracleSQL》書中對Oracle中SQL語句執行過程的截圖:


AskTom網站對hard parse、soft parse和fast parse(soft soft parse)的解釋如下:

HARD parse -- the query has neverbeen seen before, isn't in the shared pool. We must parse it, hash it, look inthe shared pool for it, don't find it, security check it, optimize it, etc(lots of work).

SOFT parse -- the query has beenseen before, is in the shared poo.  We have to parse it, hash it, look inthe shared pool for it and find it (less work then a hard parse but work nonethe less)

a kinder, softer SOFT parse -- youare using session_cached_cursors (search this site for that word forinfo).  We take your query, look in the sessions cursor cache -- find thecursor and reuse it.  Very very little work.

由此可見,一條SQL傳入Oracle後,如果它從沒有被執行過,則會經過語法、語義檢查,分析併產生執行計劃等多個步驟,耗時耗資源(hard parse);

如果這條SQL語句已經被執行過,則相應的執行計劃已被快取,Oracle僅經過語法、語義檢查即可,這條SQL的執行計劃可以在library cache中找到並直接使用;

如果Oracle設定了SESSION_CACHED_CURSORS,則SQL的執行計劃在PGA的private sql area中還好保留副本,且直接被使用,較soft parse更快。本次實驗同使用JDBC –Statement和PreparedStatement兩種SQL執行方式對SQL解析進行驗證。

試驗中使用Oracle11gR2,建立一個多欄位的表,插入50w條隨機資料。與Oracle的JDBC連線訪問使用LoadRunner Java Vuser協議呼叫Oracle提供的ojdbc6.jar驅動,SQL語句的提交通過LoadRunner的引數化儘量實現動態。壓力場景採用10併發(程序模式)執行5分鐘,Oracle AWR SnapShot採集場景執行1分鐘和4分鐘左右的結果。

二、      資料庫環境

1.       資料庫基本配置

l  Oracle資料庫相關配置引數如下:

l  optimizer_features_enable:11.2.0.1

l  optimizer_mode:ALL_ROWS

l  CPU count:4

l  memory_max_target:1232 MB

l  memory_target:1232 MB

l  sga_max_size:744 MB

l  sga_target:0 MB(Oracle自動管理)

l  session_cached_cursors:50

l  open_cursors:300

l  cursor_space_for_time:FALSE

l  cursor_sharing:EXACT

2.       建立資料庫表

CREATE TABLE HR.LOT_TEST

(

   "LOT_DATE" INTEGER NOT NULL,

   "RED_1"    INTEGER NOTNULL,

   "RED_2"    INTEGER NOTNULL,

   "RED_3"    INTEGER NOTNULL,

   "RED_4"    INTEGER NOTNULL,

    "RED_5"    INTEGER NOT NULL,

   "RED_6"    INTEGER NOTNULL,

   "BLUE"     INTEGER NOTNULL

)

ORGANIZATION HEAP

TABLESPACE USERS

NOLOGGING

PCTFREE 10

PCTUSED 40

INITRANS 1

MAXTRANS 255

STORAGE(PCTINCREASE 0

       BUFFER_POOL DEFAULT)

NOPARALLEL

CACHE

/

CREATE INDEX HR.IDX_LOT_BLUE

   ON HR.LOT_TEST("BLUE")

TABLESPACE SYSTEM

PCTFREE 10

INITRANS 2

MAXTRANS 255

STORAGE(INITIAL 64K

       PCTINCREASE 0

       BUFFER_POOL DEFAULT)

NOPARALLEL

NOCOMPRESS

/

ALTER TABLE HR.LOT_TEST

   ADD CONSTRAINT PK_LOT_DATE

   PRIMARY KEY ("LOT_DATE")

   USING INDEX TABLESPACE SYSTEM

                PCTFREE 10

                INITRANS 2

                MAXTRANS 255

                STORAGE(PCTINCREASE 0

                        BUFFER_POOL DEFAULT)

   ENABLE

   VALIDATE

/

3.       建立SEQUENCE,用以向欄位LOT_DATE中插入自增值

CREATE SEQUENCE HR.S_LOT_DATE

   START WITH 1

   INCREMENT BY 1

   NOMINVALUE

   NOMAXVALUE

   NOCYCLE

   CACHE 20

   NOORDER

/

4.       建立插入隨機資料的儲存過程

CREATE OR REPLACE PROCEDUREHR.PROC_LOT_INSERT

AS

I INT;

BEGIN

I:=1;

WHILE I<50000 LOOP

INSERT INTO HR.LOT_TESTVALUES(HR.S_LOT_DATE.NEXTVAL,DBMS_RANDOM.VALUE(1,50000),DBMS_RANDOM.VALUE(1,50000),DBMS_RANDOM.VALUE(1,10000),DBMS_RANDOM.VALUE(1,10000),DBMS_RANDOM.VALUE(1,5000),DBMS_RANDOM.VALUE(1,5000),DBMS_RANDOM.VALUE(1,50));

I:=I+1;

END LOOP;

COMMIT;

END;

插入50w條記錄,其中個欄位隨機取值的範圍不同。

三、      LoadRunner JDBC指令碼

編寫LoadRunner JDBC連線Oracle的指令碼,Run-time settings的Classpath中加入Oracle 11gR2提供的JDBC包(本例中使用的是ojdbc6.jar),同時在本機安裝1.6的JVM。查詢LOT_TEST表中的列,對查詢列的值進行隨機引數化。

1.       Statement方式

LoadRunner指令碼暫略,如有需要請和我聯絡。

2.       PreparedStatement方式

LoadRunner指令碼暫略,如有需要請和我聯絡。

四、      測試結果–限制迭代間隔

第一個測試場景限制了LoadRunnerAction的迭代間隔,在相同TPS下觀察Oracle AWR中關於解析方面的各指標值,設定10VUsers,執行5分鐘,指令碼pacing為fixed 1秒。

場景執行到1分鐘左右時手工生成第一個Oracle SnapShot,4分鐘左右生成第二個SnapShot,AWR取兩個SnapShot之間的結果:

1.       JDBC Statement

測試項

VUser數量

測試執行時間
(分鐘)

Query平均TPS
(筆/秒)

Query平均響應時間
(秒)

Statement

10

5

10

0.013

Update平均TPS
(筆/秒)

Update平均響應時間
(秒)

Insert平均TPS
(筆/秒)

Insert平均響應時間
(秒)

10

0.008

10

0.003

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

0.1

36.7

30.9

69.36

6.71

55.88

15.77

TPS:

 

響應時間:


2.       JDBC PreparedStatement

測試項

VUser數量

測試執行時間
(分鐘)

Query平均TPS
(筆/秒)

Query平均響應時間
(秒)

PreparedStatement

10

5

10

0.011

Update平均TPS
(筆/秒)

Update平均響應時間
(秒)

Insert平均TPS
(筆/秒)

Insert平均響應時間
(秒)

10

0.007

10

0.004

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

0.1

7.6

0.8

94.03

84.90

65.96

89.39

TPS:

 

響應時間:


從測試結果可以看出,由於限制了指令碼間隔,因此兩種方式下TPS、響應時間相差甚微。

五、      測試結果–不限制迭代間隔

第二個測試場景不限制LoadRunnerAction的迭代間隔,在資源消耗最大的情況下觀察OracleAWR中關於解析方面的各指標值,設定10 VUsers,執行5分鐘,指令碼pacing為fixed 1秒。

場景執行到1分鐘左右時手工生成第一個Oracle SnapShot,4分鐘左右生成第二個SnapShot,AWR取兩個SnapShot之間的結果:

1.       JDBC Statement

測試項

VUser數量

測試執行時間
(分鐘)

Query平均TPS
(筆/秒)

Query平均響應時間
(秒)

Statement

10

5

263.314

0.012

Update平均TPS
(筆/秒)

Update平均響應時間
(秒)

Insert平均TPS
(筆/秒)

Insert平均響應時間
(秒)

263.314

0.012

263.314

0.012

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

5.0

795.4

773.2

66.98

2.83

94.07

2.80

TPS:

 

響應時間:


2.       JDBC PreparedStatement

測試項

VUser數量

測試執行時間
(分鐘)

Query平均TPS
(筆/秒)

Query平均響應時間
(秒)

PreparedStatement

10

5

296.263

0.011

Update平均TPS
(筆/秒)

Update平均響應時間
(秒)

Insert平均TPS
(筆/秒)

Insert平均響應時間
(秒)

296.263

0.011

296.263

0.009

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

4.8

19.8

0.6

99.90

97.73

30.77

96.98

TPS:

 

響應時間:


從結果可以看出,由於不限制指令碼迭代的間隔,因此各場景TPS均非常高,但PreparedStatement方式下總TPS要高於Statement方式。

由於兩種方式下TPS均很高,測試表操作頻繁,因此隨著場景執行,總TPS呈下降趨勢。

六、      測試結論

1.       限制迭代間隔

在限制LoadRunner指令碼迭代間隔場景時,Statement和PreparedStatement的AWR數值對比如下:

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

Statement

0.1

36.7

30.9

69.36

6.71

55.88

15.77

PreparedStatement

0.1

7.6

0.8

94.03

84.90

65.96

89.39

DB Time per sec:由於限制了SQL語句每秒提交的次數,因此該值差別不大。

Parses per sec:由於該值包含了hard parses,因此Statement方式下該值遠高於PreparedStatement方式;

Hard parses per sec:Statement方式下該值要遠高於PreparedStatement方式。

Library Hit%:Statement方式下該值要低於PreparedStatement方式,該值的目標值是100%(本例中PreparedStatement方式下只有94%左右,一般可以理解為該值不應低於90%)

Execute to Parse %:Statement方式下該值遠低於PreparedStatement方式,Execute to Parse %=100*(1-Parses/Executions),表示SQL語句執行與解析的比例,該值越高越好,本例中Statement方式下差不多執行10條SQL就要解析9.4條,而PreparedStatement方式下基本執行10條SQL就解析1.5條(本例中該值不算很高,但實驗過程中其他輪次時PreparedStatement方式下該值可以達到97+)

Parse CPU to Parse Elapsd%:Statement方式下該值小於PreparedStatement方式,但相差不大,理論上該值越高越好。

Soft Parse%:Statement方式下該值遠低於PreparedStatement方式,該值越高越好(本例中該值不算很高,但實驗過程中其他輪次時PreparedStatement方式下該值可以達到95+)

2.       不限制迭代間隔

在不限制LoadRunner指令碼迭代間隔場景時,LoadRunner及系統指標對比如下:

測試項

VUser數量

總TPS

CPU利用率

Average Available Mbytes

Average %Disk Time

Average Disk Transfers/sec

Statement

10

1053.32

97.22%

6010.02

10.821

120.965

PreparedStatement

10

1185.117

96.426%

6447.369

32.056

242.898

由於不限制指令碼迭代間隔,因此SQL語句滿負荷消耗系統資源,CPU利用率相差無幾,但PreparedStatement方式下省去了SQL語句的解析過程,壓力都轉移到SQL的執行、結果集返回和log記錄上,Disk的利用率明顯更高。

Statement和PreparedStatement的AWR數值對比如下:

DB Time per sec

Parses per sec

Hard parses per sec

Library Hit %

Execute to Parse %

Parse CPU to Parse Elapsd %

Soft Parse %

Statement

5.0

795.4

773.2

66.98

2.83

94.07

2.80

PreparedStatement

4.8

19.8

0.6

99.90

97.73

30.77

96.98

DB Time per sec:由於不限制SQL語句每秒提交的次數,因此資源消耗較大,PreparedStatement方式下總TPS較Statement方式下高,而DB Time per sec反而小。

Parses per sec:由於該值包含了hard parses,因此Statement方式下該值遠高於PreparedStatement方式;

Hard parses per sec:Statement方式下該值要遠高於PreparedStatement方式。

Library Hit%:Statement方式下該值要低於PreparedStatement方式,該值的目標值是100%。

Execute to Parse %:Statement方式下該值遠低於PreparedStatement方式,Execute to Parse %=100*(1-Parses/Executions),表示SQL語句執行與解析的比例,該值越高越好,本例中Statement方式下差不多執行10條SQL就要解析9.7條,而PreparedStatement方式下基本執行10條SQL就解析0.2條。

Parse CPU to Parse Elapsd%:Statement方式下該值大於PreparedStatement方式,理論上該值越高越好,但從多輪實驗結果來看,PreparedStatement方式下該值確實較低,AWR報告中latch也沒有特別高,目前尚不知如何解釋。

Soft Parse%:Statement方式下該值遠低於PreparedStatement方式,該值越高越好。

3.       其他數值對比

在Oracle AWR報告的“Complete List of SQL Text”部分也能看到,PreparedStatement方式下擷取的SQL語句是帶變數的:


執行SQL語句:selectt.sql_text,t.sql_id,t.PARSE_CALLS,t.EXECUTIONS from v$sql t where sql_text like'select red_1%' or sql_text like 'update hr.lot_test%' or sql_text like 'insertinto hr.lot%';檢視SQL語句解析次數,可以看到所有SQL語句均解析且僅執行了一次:

而Statement方式下擷取的SQL語句則不帶變數,根據每次提交的查詢條件不同而不同:


執行SQL語句:selectt.sql_text,t.sql_id,t.PARSE_CALLS,t.EXECUTIONS from v$sql t where sql_text like'select red_1%' or sql_text like 'update hr.lot_test%' or sql_text like 'insertinto hr.lot%';檢視SQL語句解析次數,可以看到select、update、insert語句僅解析了一次,而執行了多次:


使用Spotlight檢視自動管理模式下SGA裡各種快取的分佈,Statement方式下shared_pool要比PreparedStatement方式佔用更多:

Statement方式下shared_pool分配了500M+的空間,而佔用率400M+:


PreparedStatement方式下shared_pool同樣分配了500M+,但僅佔用了100M+:


最後測試結果也受Oracle版本、測試環境、測試工具和筆者的理解能力所限,可能存在錯誤,僅供參考!