| 導購 | 订阅 | 在线投稿
分享
 
 
 

由于NLS參數設置不當導致的性能問題案例

來源:互聯網  2008-06-12 07:24:01  評論

這篇論壇文章(賽迪網技術社區)針對一個由于NLS參數設置不當導致的性能問題案例進行了深入的講解,更多內容請參考下文:

錯誤現象:

服務器CPU增加、響應慢。

問題原因:

取回statspack報告一看,問題很快找到了,有一條語句的physical reads非常高,初步判斷這條語句沒中索引,是全表掃描。但奇怪的是,這條語句結構很簡單,對一個表的查詢,帶一個查詢條件。類似如下:

select a, b from ttt where b like 'aaa%'

ttt表是個大表,這樣簡單而且會導致全表掃描的語句沒道理能輕易跑到生成庫上去的啊。

查了一下,果然字段b上面也是有索引的,而且b的cardinality值很高,以上語句幾乎肯定命中索引,在開發庫上看了它的查詢計劃,確實沒錯,命中了索引,效率也很高,幾乎沒有物理讀:

Execution Plan

----------------------------------------------------------

Plan hash value: 240739660

-------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 2 | 60 | 2 (0)| 00:00:01 |

| 1 | TABLE ACCESS BY INDEX ROWID| TTT | 2 | 60 | 2 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | TTT_IDX | 2 | | 1 (0)| 00:00:01 |

-------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("B" LIKE 'aaa%')

filter("B" LIKE 'aaa%')

Statistics

----------------------------------------------------------

246 recursive calls

0 db block gets

57 consistent gets

4 physical reads

0 redo size

464 bytes sent via SQL*Net to client

385 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

5 sorts (memory)

0 sorts (disk)

1 rows processed

嗯,難道是生産庫上的分析數據不正確(采用的是CBO),于是讓現場獲取語句的查詢計劃,竟然和開發庫結果是一樣的!這就奇怪了,statspack報告上顯示這條語句每次都幾百萬的物理讀啊,對比一下剛剛從生産庫拿到的報告,還是和前面一樣。難道有什麽東西改變了語句的查詢計劃?爲了證實這個想法,決定用更高級別的statspack來獲取該語句運行時的查詢計劃(具體方法可以參加另一篇文章《利用statspack來獲取生成環境中top SQL及其執行計劃》)。

先讓現場産生2個級別爲6的快照,從這兩個快照生成的報告中找到了語句的hash值,然後生成語句的報告,果然和我們得到的查詢計劃不同,是全表掃描!

SQL Text

~~~~~~~~

select a, b from ttt where b like 'aaa%'

Plans in shared pool between Begin and End Snap Ids

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Shows the Execution Plans found in the shared pool between the begin and end

snapshots specified. The values for Rows, Bytes and Cost shown below are those

which existed at the time the first-ever snapshot captured this plan - these

values often change over time, and so may not be indicative of current values

-> Rows indicates Cardinality, PHV is Plan Hash Value

-> ordered by Plan Hash Value

--------------------------------------------------------------------------------

| Operation | PHV/Object Name | Rows | Bytes| Cost |

--------------------------------------------------------------------------------

|SELECT STATEMENT | 2 | 60 | | 56 |

| TABLE ACCESS FULL | | 2 | 60 | 56 |

--------------------------------------------------------------------------------

爲什麽會這樣呢?

檢查程序代碼,結果在調用這條語句所在函數之前的語句中,我們發現了以下語句:

Execute Immediate ' alter session set nls_comp=LINGUISTIC'

Execute Immediate ' alter session set nls_sort=BINARY_CI'

嗯,這下我基本明白了。看到這兩個會話參數的設置我知道程序員是想對查詢條件不區分大小寫。這是10gR2的新特性,我也曾經在我的blog上介紹過,但是,我也提到過,這會導致查詢無法正確命中索引,這一點卻被該程序員忽略了。可以看以下結果:

SQL> alter session set nls_sort='BINARY_CI';

Session altered.

SQL> alter session set nls_comp='LINGUISTIC';

Session altered.

SQL> set autot trace

SQL> select a, b from ttt where b like 'aaa%';

Execution Plan

----------------------------------------------------------

Plan hash value: 774701505

-------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 2 | 60 | 56 (2)| 00:00:01|

|* 1 | TABLE ACCESS FULL| TTT | 2 | 60 | 56 (2)| 00:00:01|

-------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("B" LIKE 'aaa%')

Statistics

----------------------------------------------------------

364 recursive calls

0 db block gets

321 consistent gets

73 physical reads

0 redo size

560 bytes sent via SQL*Net to client

385 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

8 sorts (memory)

0 sorts (disk)

5 rows processed

SQL> alter session set nls_sort='BINARY';

Session altered.

SQL> alter session set nls_comp='BINARY';

Session altered.

SQL> select a, b from ttt where b like 'aaa%';

Execution Plan

----------------------------------------------------------

Plan hash value: 240739660

-------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 2 | 60 | 2 (0)| 00:00:01 |

| 1 | TABLE ACCESS BY INDEX ROWID| TTT | 2 | 60 | 2 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | TTT_IDX | 2 | | 1 (0)| 00:00:01 |

------------------------------------------------------------------------------

---------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("B" LIKE 'aaa%')

filter("B" LIKE 'aaa%')

Statistics

----------------------------------------------------------

8 recursive calls

0 db block gets

6 consistent gets

3 physical reads

0 redo size

464 bytes sent via SQL*Net to client

385 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed

這裏關于這個問題再說多兩句,當時我的文章中認爲通過nls設置不區分大小寫的實質是oracle內部加了upper函數,這個結論應該是錯的。實質上,此時在做精確匹配時應該是做了NLSSORT函數轉換:

SQL> alter session set nls_sort='BINARY_CI';

Session altered.

SQL> alter session set nls_comp='LINGUISTIC';

Session altered.

SQL> select a, b from ttt where b = 'aaa';

Execution Plan

----------------------------------------------------------

Plan hash value: 774701505

-------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 2 | 60 | 57 (4)| 00:00:01|

|* 1 | TABLE ACCESS FULL| TTT | 2 | 60 | 57 (4)| 00:00:01|

-------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter(NLSSORT("B",'nls_sort=''BINARY_CI''')=HEXTORAW('61616100')

)

Statistics

----------------------------------------------------------

217 recursive calls

0 db block gets

293 consistent gets

68 physical reads

0 redo size

461 bytes sent via SQL*Net to client

385 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

5 sorts (memory)

0 sorts (disk)

1 rows processed

最終,對于這個問題的解決,還是通過傳統的加upper函數來解決。

結論:

1、 不要輕易修改會話屬性,對于一個具有獨立屬性的會話,將很難定位其中的問題;

2、 新特性固然有好處,但在使用之前,一定要先了解它的負面影響。

注意,本文目的是描述問題及其解決過程,所用數據都是在試驗庫上模擬出來的。

這篇論壇文章(賽迪網技術社區)針對一個由于NLS參數設置不當導致的性能問題案例進行了深入的講解,更多內容請參考下文: 錯誤現象: 服務器CPU增加、響應慢。 問題原因: 取回statspack報告一看,問題很快找到了,有一條語句的physical reads非常高,初步判斷這條語句沒中索引,是全表掃描。但奇怪的是,這條語句結構很簡單,對一個表的查詢,帶一個查詢條件。類似如下: select a, b from ttt where b like 'aaa%' ttt表是個大表,這樣簡單而且會導致全表掃描的語句沒道理能輕易跑到生成庫上去的啊。 查了一下,果然字段b上面也是有索引的,而且b的cardinality值很高,以上語句幾乎肯定命中索引,在開發庫上看了它的查詢計劃,確實沒錯,命中了索引,效率也很高,幾乎沒有物理讀: Execution Plan ---------------------------------------------------------- Plan hash value: 240739660 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 60 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TTT | 2 | 60 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | TTT_IDX | 2 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("B" LIKE 'aaa%') filter("B" LIKE 'aaa%') Statistics ---------------------------------------------------------- 246 recursive calls 0 db block gets 57 consistent gets 4 physical reads 0 redo size 464 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 5 sorts (memory) 0 sorts (disk) 1 rows processed 嗯,難道是生産庫上的分析數據不正確(采用的是CBO),于是讓現場獲取語句的查詢計劃,竟然和開發庫結果是一樣的!這就奇怪了,statspack報告上顯示這條語句每次都幾百萬的物理讀啊,對比一下剛剛從生産庫拿到的報告,還是和前面一樣。難道有什麽東西改變了語句的查詢計劃?爲了證實這個想法,決定用更高級別的statspack來獲取該語句運行時的查詢計劃(具體方法可以參加另一篇文章《利用statspack來獲取生成環境中top SQL及其執行計劃》)。 先讓現場産生2個級別爲6的快照,從這兩個快照生成的報告中找到了語句的hash值,然後生成語句的報告,果然和我們得到的查詢計劃不同,是全表掃描! SQL Text ~~~~~~~~ select a, b from ttt where b like 'aaa%' Plans in shared pool between Begin and End Snap Ids ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Shows the Execution Plans found in the shared pool between the begin and end snapshots specified. The values for Rows, Bytes and Cost shown below are those which existed at the time the first-ever snapshot captured this plan - these values often change over time, and so may not be indicative of current values -> Rows indicates Cardinality, PHV is Plan Hash Value -> ordered by Plan Hash Value -------------------------------------------------------------------------------- | Operation | PHV/Object Name | Rows | Bytes| Cost | -------------------------------------------------------------------------------- |SELECT STATEMENT | 2 | 60 | | 56 | | TABLE ACCESS FULL | | 2 | 60 | 56 | -------------------------------------------------------------------------------- 爲什麽會這樣呢? 檢查程序代碼,結果在調用這條語句所在函數之前的語句中,我們發現了以下語句: Execute Immediate ' alter session set nls_comp=LINGUISTIC' Execute Immediate ' alter session set nls_sort=BINARY_CI' 嗯,這下我基本明白了。看到這兩個會話參數的設置我知道程序員是想對查詢條件不區分大小寫。這是10gR2的新特性,我也曾經在我的blog上介紹過,但是,我也提到過,這會導致查詢無法正確命中索引,這一點卻被該程序員忽略了。可以看以下結果: SQL> alter session set nls_sort='BINARY_CI'; Session altered. SQL> alter session set nls_comp='LINGUISTIC'; Session altered. SQL> set autot trace SQL> select a, b from ttt where b like 'aaa%'; Execution Plan ---------------------------------------------------------- Plan hash value: 774701505 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 60 | 56 (2)| 00:00:01| |* 1 | TABLE ACCESS FULL| TTT | 2 | 60 | 56 (2)| 00:00:01| ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("B" LIKE 'aaa%') Statistics ---------------------------------------------------------- 364 recursive calls 0 db block gets 321 consistent gets 73 physical reads 0 redo size 560 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 8 sorts (memory) 0 sorts (disk) 5 rows processed SQL> alter session set nls_sort='BINARY'; Session altered. SQL> alter session set nls_comp='BINARY'; Session altered. SQL> select a, b from ttt where b like 'aaa%'; Execution Plan ---------------------------------------------------------- Plan hash value: 240739660 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 60 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TTT | 2 | 60 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | TTT_IDX | 2 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------ --------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("B" LIKE 'aaa%') filter("B" LIKE 'aaa%') Statistics ---------------------------------------------------------- 8 recursive calls 0 db block gets 6 consistent gets 3 physical reads 0 redo size 464 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed 這裏關于這個問題再說多兩句,當時我的文章中認爲通過nls設置不區分大小寫的實質是oracle內部加了upper函數,這個結論應該是錯的。實質上,此時在做精確匹配時應該是做了NLSSORT函數轉換: SQL> alter session set nls_sort='BINARY_CI'; Session altered. SQL> alter session set nls_comp='LINGUISTIC'; Session altered. SQL> select a, b from ttt where b = 'aaa'; Execution Plan ---------------------------------------------------------- Plan hash value: 774701505 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 60 | 57 (4)| 00:00:01| |* 1 | TABLE ACCESS FULL| TTT | 2 | 60 | 57 (4)| 00:00:01| ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(NLSSORT("B",'nls_sort=''BINARY_CI''')=HEXTORAW('61616100') ) Statistics ---------------------------------------------------------- 217 recursive calls 0 db block gets 293 consistent gets 68 physical reads 0 redo size 461 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 5 sorts (memory) 0 sorts (disk) 1 rows processed 最終,對于這個問題的解決,還是通過傳統的加upper函數來解決。 結論: 1、 不要輕易修改會話屬性,對于一個具有獨立屬性的會話,將很難定位其中的問題; 2、 新特性固然有好處,但在使用之前,一定要先了解它的負面影響。 注意,本文目的是描述問題及其解決過程,所用數據都是在試驗庫上模擬出來的。
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有