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