Consistent gets 的理解

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時,就變成了全表掃描。因爲優化器估算出使用索引可能比全部掃描訪問的塊更多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章