Consistent gets 的理解

来源:互联网 发布:古玩玉器交友软件 编辑:程序博客网 时间:2024/05/22 18:48

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 arrayarraysize 10SQL> set array 15array和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 processedSQL> 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时,就变成了全表扫描。因为优化器估算出使用索引可能比全部扫描访问的块更多。

原创粉丝点击