最近朋友遇到一个问题,有个表迁到11g后,之前设置buffer_pool keep失效了,每次查询都有很大的物理读
经过检查发现是11g的新特性direct path read导致的
这个特性其实就是oracle在扫描一个大表的时候,会将数据直接读取到pga中,而不经过sga,所以就造成了buffer_pool keep没有生效的问题
oracle通过参数_small_table_threshold 来评估一个表是大表还是小表,单位是块,大于这个值的表,oracle就认为是大表
下面我们模拟一下这种情况
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL> create table goolen as select * from dba_objects;
Table created.
SQL> alter table scott.goolen storage(buffer_pool keep);
Table altered.
SQL> insert into goolen select * from goolen;
SQL> /
/....
--这个sql可以查看buffer_cache中的缓存对象
SQL> SELECT o.object_name, COUNT(1) number_of_blocks
2 FROM DBA_OBJECTS o, V$BH bh
3 WHERE o.object_id = bh.objd
4 AND o.owner != 'SYS'
5 group by o.object_name
6 ORDER BY count(1);
OBJECT_NAME NUMBER_OF_BLOCKS
----------------------------------- ----------------
GOOLEN 31560
--查看参数_small_table_threshold的值
SQL> col name for a30
SQL> col value for a20
SQL> col DESCRIB for a60
SQL> set linesize 140 pagesize 1400
SQL>
SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ
2 FROM SYS.x$ksppi x, SYS.x$ksppcv y
3 WHERE x.inst_id = USERENV ('Instance')
4 AND y.inst_id = USERENV ('Instance')
5 AND x.indx = y.indx
6 AND (x.ksppinm ='_small_table_threshold'or x.ksppinm='_serial_direct_read');
NAME VALUE DESCRIB
------------------------------ -------------------- ------------------------------------------------------------
_small_table_threshold 500 lower threshold level of table size for direct reads
_serial_direct_read FALSE enable direct read in serial
SQL> alter system flush buffer_cache;
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16499 consistent gets
16489 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16499 consistent gets
16489 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
--每次查询都有很大的物理读
--我们用10046事件跟踪一下这个sql
SQL> oradebug setmypid
Statement processed.
SQL> oradebug tracefile_name
/opt/app/oracle/diag/rdbms/goolen/goolen/trace/goolen_ora_2194.trc
SQL> alter session set events '10046 trace name context forever,level 8';
Session altered.
SQL> select count(*) from scott.goolen;
COUNT(*)
----------
1155424
--查看trace文件发现wait都是direct path read
=====================
PARSING IN CURSOR #2 len=33 dep=0 uid=0 oct=3 lid=0 tim=1382688719747413 hv=1981856655 ad='db29f650' sqlid='4ub9jxdv21fwg'
select count(*) from scott.goolen
END OF STMT
PARSE #2:c=8998,e=9807,p=270,cr=72,cu=0,mis=1,r=0,dep=0,og=1,plh=1270414684,tim=1382688719747412
EXEC #2:c=0,e=34,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1270414684,tim=1382688719747526
WAIT #2: nam='SQL*Net message to client' ela= 2 driver id=1650815232 #bytes=1 p3=0 obj#=5352 tim=1382688719747576
WAIT #2: nam='direct path read' ela= 128 file number=4 first dba=171 block cnt=13 obj#=74754 tim=1382688719748396
WAIT #2: nam='direct path read' ela= 138 file number=4 first dba=185 block cnt=15 obj#=74754 tim=1382688719748590
WAIT #2: nam='direct path read' ela= 64 file number=4 first dba=201 block cnt=15 obj#=74754 tim=1382688719748779
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=217 block cnt=15 obj#=74754 tim=1382688719748954
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=233 block cnt=15 obj#=74754 tim=1382688719749128
WAIT #2: nam='direct path read' ela= 53 file number=4 first dba=249 block cnt=15 obj#=74754 tim=1382688719749305
WAIT #2: nam='direct path read' ela= 62 file number=4 first dba=265 block cnt=15 obj#=74754 tim=1382688719749488
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=281 block cnt=15 obj#=74754 tim=1382688719749660
WAIT #2: nam='direct path read' ela= 1069 file number=4 first dba=386 block cnt=126 obj#=74754 tim=1382688719750861
WAIT #2: nam='direct path read' ela= 1058 file number=4 first dba=514 block cnt=126 obj#=74754 tim=1382688719752056
WAIT #2: nam='direct path read' ela= 411 file number=4 first dba=642 block cnt=126 obj#=74754 tim=1382688719753315
WAIT #2: nam='direct path read' ela= 421 file number=4 first dba=770 block cnt=126 obj#=74754 tim=1382688719754560
WAIT #2: nam='direct path read' ela= 413 file number=4 first dba=898 block cnt=126 obj#=74754 tim=1382688719755788
WAIT #2: nam='direct path read' ela= 405 file number=4 first dba=1026 block cnt=126 obj#=74754 tim=1382688719757010
WAIT #2: nam='direct path read' ela= 400 file number=4 first dba=1154 block cnt=126 obj#=74754 tim=1382688719758225
WAIT #2: nam='direct path read' ela= 416 file number=4 first dba=1282 block cnt=126 obj#=74754 tim=1382688719759478
WAIT #2: nam='direct path read' ela= 410 file number=4 first dba=1410 block cnt=126 obj#=74754 tim=1382688719760723
--我们可以通过调大此参数的值,来避免oracle将这个表视为大表
SQL> alter session set "_small_table_threshold"=9999999;
Session altered.
--当然也可以通过设置10949事件来禁止这个特性
-- ALTER SESSION SET EVENTS '10949 TRACE NAME CONTEXT FOREVER';
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
SQL> set autot on
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16506 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16506 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
经过检查发现是11g的新特性direct path read导致的
这个特性其实就是oracle在扫描一个大表的时候,会将数据直接读取到pga中,而不经过sga,所以就造成了buffer_pool keep没有生效的问题
oracle通过参数_small_table_threshold 来评估一个表是大表还是小表,单位是块,大于这个值的表,oracle就认为是大表
下面我们模拟一下这种情况
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL> create table goolen as select * from dba_objects;
Table created.
SQL> alter table scott.goolen storage(buffer_pool keep);
Table altered.
SQL> insert into goolen select * from goolen;
SQL> /
/....
--这个sql可以查看buffer_cache中的缓存对象
SQL> SELECT o.object_name, COUNT(1) number_of_blocks
2 FROM DBA_OBJECTS o, V$BH bh
3 WHERE o.object_id = bh.objd
4 AND o.owner != 'SYS'
5 group by o.object_name
6 ORDER BY count(1);
OBJECT_NAME NUMBER_OF_BLOCKS
----------------------------------- ----------------
GOOLEN 31560
--查看参数_small_table_threshold的值
SQL> col name for a30
SQL> col value for a20
SQL> col DESCRIB for a60
SQL> set linesize 140 pagesize 1400
SQL>
SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ
2 FROM SYS.x$ksppi x, SYS.x$ksppcv y
3 WHERE x.inst_id = USERENV ('Instance')
4 AND y.inst_id = USERENV ('Instance')
5 AND x.indx = y.indx
6 AND (x.ksppinm ='_small_table_threshold'or x.ksppinm='_serial_direct_read');
NAME VALUE DESCRIB
------------------------------ -------------------- ------------------------------------------------------------
_small_table_threshold 500 lower threshold level of table size for direct reads
_serial_direct_read FALSE enable direct read in serial
SQL> alter system flush buffer_cache;
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16499 consistent gets
16489 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16499 consistent gets
16489 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
--每次查询都有很大的物理读
--我们用10046事件跟踪一下这个sql
SQL> oradebug setmypid
Statement processed.
SQL> oradebug tracefile_name
/opt/app/oracle/diag/rdbms/goolen/goolen/trace/goolen_ora_2194.trc
SQL> alter session set events '10046 trace name context forever,level 8';
Session altered.
SQL> select count(*) from scott.goolen;
COUNT(*)
----------
1155424
--查看trace文件发现wait都是direct path read
=====================
PARSING IN CURSOR #2 len=33 dep=0 uid=0 oct=3 lid=0 tim=1382688719747413 hv=1981856655 ad='db29f650' sqlid='4ub9jxdv21fwg'
select count(*) from scott.goolen
END OF STMT
PARSE #2:c=8998,e=9807,p=270,cr=72,cu=0,mis=1,r=0,dep=0,og=1,plh=1270414684,tim=1382688719747412
EXEC #2:c=0,e=34,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1270414684,tim=1382688719747526
WAIT #2: nam='SQL*Net message to client' ela= 2 driver id=1650815232 #bytes=1 p3=0 obj#=5352 tim=1382688719747576
WAIT #2: nam='direct path read' ela= 128 file number=4 first dba=171 block cnt=13 obj#=74754 tim=1382688719748396
WAIT #2: nam='direct path read' ela= 138 file number=4 first dba=185 block cnt=15 obj#=74754 tim=1382688719748590
WAIT #2: nam='direct path read' ela= 64 file number=4 first dba=201 block cnt=15 obj#=74754 tim=1382688719748779
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=217 block cnt=15 obj#=74754 tim=1382688719748954
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=233 block cnt=15 obj#=74754 tim=1382688719749128
WAIT #2: nam='direct path read' ela= 53 file number=4 first dba=249 block cnt=15 obj#=74754 tim=1382688719749305
WAIT #2: nam='direct path read' ela= 62 file number=4 first dba=265 block cnt=15 obj#=74754 tim=1382688719749488
WAIT #2: nam='direct path read' ela= 52 file number=4 first dba=281 block cnt=15 obj#=74754 tim=1382688719749660
WAIT #2: nam='direct path read' ela= 1069 file number=4 first dba=386 block cnt=126 obj#=74754 tim=1382688719750861
WAIT #2: nam='direct path read' ela= 1058 file number=4 first dba=514 block cnt=126 obj#=74754 tim=1382688719752056
WAIT #2: nam='direct path read' ela= 411 file number=4 first dba=642 block cnt=126 obj#=74754 tim=1382688719753315
WAIT #2: nam='direct path read' ela= 421 file number=4 first dba=770 block cnt=126 obj#=74754 tim=1382688719754560
WAIT #2: nam='direct path read' ela= 413 file number=4 first dba=898 block cnt=126 obj#=74754 tim=1382688719755788
WAIT #2: nam='direct path read' ela= 405 file number=4 first dba=1026 block cnt=126 obj#=74754 tim=1382688719757010
WAIT #2: nam='direct path read' ela= 400 file number=4 first dba=1154 block cnt=126 obj#=74754 tim=1382688719758225
WAIT #2: nam='direct path read' ela= 416 file number=4 first dba=1282 block cnt=126 obj#=74754 tim=1382688719759478
WAIT #2: nam='direct path read' ela= 410 file number=4 first dba=1410 block cnt=126 obj#=74754 tim=1382688719760723
--我们可以通过调大此参数的值,来避免oracle将这个表视为大表
SQL> alter session set "_small_table_threshold"=9999999;
Session altered.
--当然也可以通过设置10949事件来禁止这个特性
-- ALTER SESSION SET EVENTS '10949 TRACE NAME CONTEXT FOREVER';
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
SQL> set autot on
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16506 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from goolen;
COUNT(*)
----------
1155424
Execution Plan
----------------------------------------------------------
Plan hash value: 1270414684
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4771 (2)| 00:00:58 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| GOOLEN | 19M| 4771 (2)| 00:00:58 |
---------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16506 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed