Consistent gets 与db block gets的区别很明显,后者指DML访问,前者指SELECT访问,这里的一致性读不表示从回滚块组织一致性数据,意思是以保证一致性为目标的读。DML不需要保证一致性,所以直接读data block。
Consistent gets 的值是怎么得来,有何意义,现在也并不很明白。计算公式差不多等于:
SELECT COUNT(*) FROM T5;得到的Consistent gets + (结果集的行数rows / arraySize);
也有很多COPY一遍又一遍的文章说是rows/arraySize + blocks
指代的意义,是这次查询扫描了几次数据块,并不是几个数据块。开始我一直没想到这里。
现在的理解还是比较模糊,仅供参考:
在没有索引的表上面条件查询时,先进行全表扫描,得到完整结果集,服务端组织数据到SQLPLUS显示,由于设置了arraySize分页,默认15,那么服务器与客户端网络上往返的次数会跟这个arraySize有关。如果<15条数据,得到一次,如果是50条数据,得到4次。再加上count得到的值。
但是大数据量的时候这个公式还是不准确。
create table T5
(
id NUMBER(9),
age NUMBER(9),
name VARCHAR2(888)
)
begin
for i in 1..50
loop
insert into t5 values(i,99,'TOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMYTOMMY');
END LOOP;
COMMIT;
END;
查数据库分布:
SQL> SELECT dbms_rowid.rowid_block_number(ROWID) L,COUNT(*) FROM T5 GROUP BY dbms_rowid.rowid_block_number(ROWID);
L COUNT(*)
---------- ----------
189 12
191 12
188 2
190 12
192 12
SQL> select blocks from user_segments where segment_name ='T5';
BLOCKS
----------
8
SQL> SELECT blocks,empty_blocks FROM USER_TABLES T WHERE T.TABLE_NAME='T5';
BLOCKS EMPTY_BLOCKS
---------- ------------
5 3
总共50条记录。分布在5个数据块。另外有三个空块。
但是,上面第一个使用ROWID_BLOCK_NUMBER的查询在数据量大的时候与USER_TABLES表明的块数量会不一样。这些是有差别的。
进入SQLPLUS
多次查询保证没有物理读。
SQL> set autotrace traceonly statistics;
SQL> select count(*) from t5;
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
408 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
COUNT(*) = 7。这7个里面,先是访问了5个数据块,从块头读到记录行数(或者扫描块体),另外的两个就不得而知了。
当进行条件查询时,仍然进行全表扫描,这时就以这个7为基数,然后加上因为arraySize导致的客户端服务端往返的重复扫描同一个块的次数。
所以过程可能是:第一步执行所有数据块的全部扫描,得到7次,然后会确定结果集的分布情况,分布在哪些数据块里面,接下来再到这些块里面提取行,提取的过程因为arraySize所以可能会多次进入同一个块提取。
(===================如果是这样:第一步扫描完所有块,得到一个结果集,服务端保留这个结果集为临时数据,然后根据arraySize分成一次或几次发送到客户端,那么后面几次到服务端取数据时就不用访问数据块,所以第一步应该只是确定数据分布吧)
SQL> show array
arraysize 10
SQL> set array 15
array和arraysize是一样的
SQL> select * from t5;
已选择50行。
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
11 consistent gets
0 physical reads
0 redo size
29829 bytes sent via SQL*Net to client
418 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
50 rows processed
然后改arraysize。重新查询。依次得到
1/2>>32gets
3>>24
4>>20
5/6>>16
7/8>>14
9>>13
10/11/12>>12
13>>10
14/15>>11
20>>10
50>>8
100>>8
这里array=1时只出现了32次,可能是有一些细节处理的机制。因为这些机制,特别是大数据量的时候就不一定准确了。
接下来条件查询:
SQL> select * from t5 where id<=1;
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
1077 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
SQL> select * from t5 where id<=15;
已选择15行。
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
1220 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)
15 rows processed
改条件,依次得到:
id<=15---------------------------------------8
15<id<=30 ---------------------------------------9
30<id<=45 ---------------------------------------10
45<id ---------------------------------------11
所以,关键应该在于数据集通过arraySize分页的过程中。
另外还有一个fetchSize,SQLPLUS里面没有这个值,JAVA里面可以设置,好像是一个用处
下面设置索引
SQL> CREATE INDEX I_1 ON T5(ID);
索引已创建。
全部查询统计信息还是一样。COUNT(*) 也没有变。
条件查询就不一样了。
SQL> SELECT * FROM T5 WHERE ID=5;
执行计划
----------------------------------------------------------
Plan hash value: 4059149231
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 564 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T5 | 1 | 564 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I_1 | 1 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=5)
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1083 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
这里数据块可能只扫描了一次,而索引块扫描了两次。
所以关键之处,应该在于理解服务端提取数据的步骤。分成两步,第一步是服务端查询出所有结果集,第二步是分次数发送到客户端。
SQL> SELECT * FROM T5 WHERE ID<=20;
已选择20行。
执行计划
----------------------------------------------------------
Plan hash value: 2002323537
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 11280 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T5 | 20 | 11280 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"<=20)
统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
9 consistent gets
0 physical reads
0 redo size
1387 bytes sent via SQL*Net to client
396 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
20 rows processed
当ID<=19时还是选择的索引范围扫描,当ID<=20时,就变成了全表扫描。因为优化器估算出使用索引可能比全部扫描访问的块更多。