且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

20150209]为什么少1个逻辑读.txt

更新时间:2022-08-15 08:19:44

[20150209]为什么少1个逻辑读.txt

--昨天看vage的,提到1个奇怪的问题,作者也清楚为什么?我自己也困惑.
--把它记录下来.

1.建立测试环境:
SCOTT@test> @ver1

PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.3.0     Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

create table t1 as select rownum id,'test' data from dual connect by levelcreate table t2 as select rownum id,'test' data from dual connect by levelexec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't1',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false);
exec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't2',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false);

SCOTT@test> show array
arraysize 200

SCOTT@test> alter session set statistics_level=all;
Session altered.

SCOTT@test> select * from t1;
        ID DATA
---------- ----
         1 test

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  27uhu2q2xuu7r, child number 0
-------------------------------------
select * from t1
Plan hash value: 3617692013
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     3 (100)|      1 |00:00:00.01 |       2 |
|   1 |  TABLE ACCESS FULL| T1   |      1 |      1 |     3   (0)|      1 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------------
13 rows selected.

SCOTT@test> select * from t2;
        ID DATA
---------- ----
         1 test
         2 test

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  741xfy549bxbz, child number 0
-------------------------------------
select * from t2
Plan hash value: 1513984157
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     3 (100)|      2 |00:00:00.01 |       3 |
|   1 |  TABLE ACCESS FULL| T2   |      1 |      2 |     3   (0)|      2 |00:00:00.01 |       3 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--注意看buffers的数量,一个是2,多了1条记录的情况下是3,很奇怪吧.而且在仅仅1条记录的情况下,仅仅读取段头1次.而不是作者讲的2
--次. 不知道是否这个版本特殊.还是oracle做了什么特殊优化.
--如果在10g,12c下测试(使用ctas建表),全表扫描T1逻辑读是3,全表扫描T2逻辑读是4. 不管怎样逻辑读是相差1.
--按照vage的书中介绍(P155),在整个全表扫描,整个表的第1行,会单独占用1个逻辑读.这样在2条记录的情况下存在2个逻辑读.
  加上段头1个逻辑读就3个.

--至于为什么,我也不清楚,作者也不清楚.不过可以做一个例子来验证这种情况.

SCOTT@test> create table t3 as select rownum id,'test' data from dual connect by levelTable created.

SCOTT@test> alter table t3 minimize records_per_block;
Table altered.

--这样就实现每块100条记录.

SCOTT@test> insert into t3  select 100+rownum id,'test' data from dual connect by level400 rows created.

SCOTT@test> commit ;
Commit complete.

--这样总共插入100条记录. 正好占满一个1个extents.(我使用assm,前面有L1,L2,以及段头),剩下仅仅5块.

SCOTT@test> select * from dba_extents where owner=user and segment_name='T3';
OWNER  SEGMENT_NAME  SEGMENT_TYPE  TABLESPACE_NAME  EXTENT_ID    FILE_ID   BLOCK_ID      BYTES     BLOCKS RELATIVE_FNO
------ ------------- ------------- ---------------- --------- ---------- ---------- ---------- ---------- ------------
SCOTT  T3            TABLE         USERS                    0          4        536      65536          8            4

--如果我设置array=100,可以想象逻辑读的情况,跨块的算2个逻辑读:
读1,99,1,99,1,99,1,99,1,99,这样在加上块头1个逻辑读(我使用11g的版本),应该是11个逻辑读.

SCOTT@test> @stats t3
exec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't3',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false)
PL/SQL procedure successfully completed.
--注意要分析表,避免动态取样.

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |      11 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |      11 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--可以发现我的猜测一直.

--如果我设置array=98呢?
--读1,98,1 ,98,2, 96,4, 94,6, 92,8,这样在加上块头1个逻辑读(我使用11g的版本),应该是12个逻辑读.

SCOTT@test> set array 98

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |      12 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |      12 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--如果设置array=101呢?
--读1,99, 2,98, 3,97, 4,96, 5,96这样在加上块头1个逻辑读(我使用11g的版本),应该是11个逻辑读.

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |      11 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |      11 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--如果设置array=200呢?
--读1,99, 100, 1,99 100, 1,99 这样在加上块头1个逻辑读(我使用11g的版本),应该是9个逻辑读.

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |       9 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |       9 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--如果设置array=498呢?
--读1,99, 100, 100 100, 99,1 这样在加上块头1个逻辑读(我使用11g的版本),应该是8个逻辑读.
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |       8 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |       8 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--如果设置array=499呢?
--读1,99, 100, 100 100, 100 这样在加上块头1个逻辑读(我使用11g的版本),应该是7个逻辑读.

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8xs59s0kcpn57, child number 0
-------------------------------------
select * from t3
Plan hash value: 4161002650
-------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     4 (100)|    500 |00:00:00.01 |       7 |
|   1 |  TABLE ACCESS FULL| T3   |      1 |    500 |     4   (0)|    500 |00:00:00.01 |       7 |
-------------------------------------------------------------------------------------------------
13 rows selected.

--再设置大一些,最少也需要7个逻辑读.

总结:
1.在全表扫描是表的第一行自成1批,算1个逻辑读.
2.不知道11g做了什么处理,仅仅读1次段头.
3.理论讲设置大一些array参数,可以减少逻辑读.