【基礎篇一致性讀】一致性讀分析

yellowlee發表於2010-07-17

對執行計劃和相關的一致性讀簡單分析一下。

通常,我們使用set autotrace on來看執行計劃和執行的統計資訊。按照前面一片文章的例子示意:

SQL> select count(*) from t_test_notequ1 a where a.object_id =2;

 

  COUNT(*)

----------

     59622

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 2434651730

 

--------------------------------------------------------------------------------

 

----------

 

| Id  | Operation             | Name             | Rows  | Bytes | Cost (%CPU)|

Time     |

 

--------------------------------------------------------------------------------

 

----------

 

|   0 | SELECT STATEMENT      |                  |     1 |     2 |    78   (3)|

00:00:01 |

 

|   1 |  SORT AGGREGATE       |                  |     1 |     2 |            |

         |

 

|*  2 |   INDEX FAST FULL SCAN| IDX_TEST_NOTEQU1 | 59323 |   115K|    78   (3)|

00:00:01 |

 

--------------------------------------------------------------------------------

 

----------

 

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   2 - filter("A"."OBJECT_ID"=2)

 

 

統計資訊

----------------------------------------------------------

         96  recursive calls

          0  db block gets

        359  consistent gets

          0  physical reads

          0  redo size

        410  bytes sent via SQL*Net to client

        385  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          3  sorts (memory)

          0  sorts (disk)

1   rows processed

 

這裡只關注上面的黑體部分即統計資訊,先來看看recursive calls

我們知道在很多情況下會產生recursive calls(遞迴呼叫),比如做一些操作的分配空間,執行ddl,或者觸發trigger,或者在解析的時候資料字典快取需要一些物件的資訊。這裡只看select的情況,因此本例中可以認為主要是因為需要資料字典資訊,解析和分配空間導致了遞迴呼叫的產生。

Db block gets

Oracle文件這麼給出來的:

Number of times a CURRENT block was requested.

這裡主要只看query modeblock,也就是在select過程中block並沒有被其他session修改。

consistent getsNumber of times a consistent read was requested for a block.

Oracle利用undo來實現了一致性讀,保證從查詢時間點開始,所有讀取的block都是時間一致的,如果這段時間內block變化了則要前映象這個block,然後再讀取資料。

可以認為一致性讀就是從databuffer中讀取的次數,後面詳細給出計算方法。

physical reads

很好理解,從磁碟或者io快取中讀取的次數

redo size

產生的redo數量。產生redo log情況有很多種,這個例子中只看select產生的情況,這種情況很多時候是oracle的延遲塊清除,僅僅提一下,後面有一個實驗可以具體看到現象。

先主要只看上面的幾個。

 

SQL> alter system flush shared_pool;

 

系統已更改。

 

SQL> alter system flush buffer_cache;

 

系統已更改。

 

先從全表掃描來看:

SQL> select count(*) from t_test_notequ1 a;

 

  COUNT(*)

----------

     59623

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 360092435

 

-----------------------------------------------------------------------------

| Id  | Operation          | Name           | Rows  | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |                |     1 |   183   (2)| 00:00:03 |

|   1 |  SORT AGGREGATE    |                |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |   183   (2)| 00:00:03 |

-----------------------------------------------------------------------------

 

 

統計資訊

----------------------------------------------------------

        171  recursive calls

          0  db block gets

        845  consistent gets

        823  physical reads

          0  redo size

        410  bytes sent via SQL*Net to client

        385  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          3  sorts (memory)

          0  sorts (disk)

          1  rows processed

 

SQL>

 

多執行幾次,讓recursive callsphysical reads都變為零,即完全從記憶體中讀取。

SQL> select count(*) from t_test_notequ1 a;

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 360092435

 

-----------------------------------------------------------------------------

| Id  | Operation          | Name           | Rows  | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |                |     1 |   183   (2)| 00:00:03 |

|   1 |  SORT AGGREGATE    |                |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |   183   (2)| 00:00:03 |

-----------------------------------------------------------------------------

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

        821  consistent gets

          0  physical reads

          0  redo size

        410  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

可以看到至少需要821個邏輯讀,或者821個讀block操作。

 

對於全表掃描一個表獲取全部行產生的一致性讀的一個計算公式:

consistent gets =numrows/arraysize + blocks

其中numrows為行數,blocks是塊數

SQL> select a.blocks,a.num_rows,a.empty_blocks

  2  from dba_tables a where a.table_name = upper ('t_test_notequ1');

 

    BLOCKS   NUM_ROWS EMPTY_BLOCKS

---------- ---------- ------------

       816      59623           79

SQL> show arraysize

arraysize 15

則有:

SQL> select 59623/15+821 from dual;

 

59623/15+821

------------

4795.8666666

下面不用count(*),以免輸出資訊太多,trace模式設定為traceonly

SQL> select * from t_test_notequ1 a;

 

已選擇59623行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 1605230170

 

--------------------------------------------------------------------------------

 

----

 

| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time

   |

 

--------------------------------------------------------------------------------

 

----

 

|   0 | SELECT STATEMENT  |                | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

|   1 |  TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

--------------------------------------------------------------------------------

 

----

 

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

       4751  consistent gets

          0  physical reads

          0  redo size

    6276062  bytes sent via SQL*Net to client

      44099  bytes received via SQL*Net from client

       3976  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59623  rows processed

 

SQL>

 

可以看出這個數字基本很接近了。

現在在另外一個session進行一個update操作,但並不提交:

SQL> update  t_test_notequ1 set object_id = 2 where rownum = 1;

 

已更新 1 行。

 

多執行幾次使得recursive calls將為0,看到一致性讀增加了1

 

SQL> select * from t_test_notequ1 a;

 

已選擇59623行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 1605230170

 

--------------------------------------------------------------------------------

 

----

 

| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time

   |

 

--------------------------------------------------------------------------------

 

----

 

|   0 | SELECT STATEMENT  |                | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

|   1 |  TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

--------------------------------------------------------------------------------

 

----

 

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

       4752  consistent gets

          0  physical reads

          0  redo size

    6276062  bytes sent via SQL*Net to client

      44099  bytes received via SQL*Net from client

       3976  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59623  rows processed

 

SQL>

 

而將update操作commit之後,又成了4751

SQL> select * from t_test_notequ1 a;

 

已選擇59623行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 1605230170

 

--------------------------------------------------------------------------------

 

----

 

| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time

   |

 

--------------------------------------------------------------------------------

 

----

 

|   0 | SELECT STATEMENT  |                | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

|   1 |  TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

--------------------------------------------------------------------------------

 

----

 

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

       4751  consistent gets

          0  physical reads

          0  redo size

    6276062  bytes sent via SQL*Net to client

      44099  bytes received via SQL*Net from client

       3976  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59623  rows processed

 

SQL>

 

再來看看修改sqlplusarraysize之後的情況:

SQL> select * from t_test_notequ1 a;

 

已選擇59623行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 1605230170

 

--------------------------------------------------------------------------------

 

----

 

| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time

   |

 

--------------------------------------------------------------------------------

 

----

 

|   0 | SELECT STATEMENT  |                | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

|   1 |  TABLE ACCESS FULL| T_TEST_NOTEQU1 | 59623 |  4890K|   185   (3)| 00:00:

 

03 |

 

--------------------------------------------------------------------------------

 

----

 

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

        880  consistent gets

          0  physical reads

          0  redo size

    5767112  bytes sent via SQL*Net to client

       1034  bytes received via SQL*Net from client

         61  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59623  rows processed

 

SQL>

而:

SQL> select 59623/1000+821 from dual;

 

59623/1000+821

--------------

       880.623

進一步說明了這個計算公式大致符合。

 

再來看看使用index的情況:

SQL> select * from t_test_notequ1 a where a.object_id = 1;

 

未選定行

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 3227361085

 

--------------------------------------------------------------------------------

 

----------------

 

| Id  | Operation                   | Name             | Rows  | Bytes | Cost (%

 

CPU)| Time     |

 

--------------------------------------------------------------------------------

 

----------------

 

|   0 | SELECT STATEMENT            |                  |     1 |    84 |     2

 (0)| 00:00:01 |

 

|   1 |  TABLE ACCESS BY INDEX ROWID| T_TEST_NOTEQU1   |     1 |    84 |     2

 (0)| 00:00:01 |

 

|*  2 |   INDEX RANGE SCAN          | IDX_TEST_NOTEQU1 |     1 |       |     1

 (0)| 00:00:01 |

 

--------------------------------------------------------------------------------

 

----------------

 

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   2 - access("A"."OBJECT_ID"=1)

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

          2  consistent gets

          0  physical reads

          0  redo size

        992  bytes sent via SQL*Net to client

        374  bytes received via SQL*Net from client

          1  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          0  rows processed

 

SQL>

這裡的2個一致性讀也很好理解,btree高度為1 ,先找branch block 然後找leaf block兩次均未找到值,直接返回,2個邏輯讀。

修改一下查詢object_id=2的所有行:

SQL> select /*+ index(a)*/* from t_test_notequ1 a where a.object_id > 1;

 

已選擇59622行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 3227361085

 

--------------------------------------------------------------------------------

 

----------------

 

| Id  | Operation                   | Name             | Rows  | Bytes | Cost (%

 

CPU)| Time     |

 

--------------------------------------------------------------------------------

 

----------------

 

|   0 | SELECT STATEMENT            |                  | 59622 |  4890K|  1162

 (1)| 00:00:14 |

 

|   1 |  TABLE ACCESS BY INDEX ROWID| T_TEST_NOTEQU1   | 59622 |  4890K|  1162

 (1)| 00:00:14 |

 

|*  2 |   INDEX RANGE SCAN          | IDX_TEST_NOTEQU1 | 59622 |       |   341

 (1)| 00:00:05 |

 

--------------------------------------------------------------------------------

 

----------------

 

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   2 - access("A"."OBJECT_ID">1)

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

       1273  consistent gets

          0  physical reads

          0  redo size

    5767021  bytes sent via SQL*Net to client

       1034  bytes received via SQL*Net from client

         61  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59622  rows processed

 

SQL>

 

前面的操作中將一條資料的object_id更新為了null,而null值不被索引,從下面獲得索引的高度和block數:

SQL> select a.blevel,a.leaf_blocks,a.num_rows

  2   from dba_indexes a where a.index_name = upper ('idx_test_notequ1');

 

    BLEVEL LEAF_BLOCKS   NUM_ROWS

---------- ----------- ----------

         1         338      59622

SQL> select a.blocks from dba_segments a

  2  where a.segment_name = upper ('idx_test_notequ1');

 

    BLOCKS

----------

       384

 

套用一下前面提到的公式:

SQL> select (59622/1000+384)*2 + 384 from dual;

 

(59622/1000+384)*2+384

----------------------

              1271.244

這裡的計算先做一個推斷,btree高度為1,則每找到一個rowid先訪問branch block找到leafblock,再從leaf block中找到rowid,資料塊都已經在快取中了,直接讀取這些塊。

修改一下arraysize,很容易驗證這個推斷:

SQL> set arraysize 100;

 

SQL> select /*+ index(a)*/* from t_test_notequ1 a where a.object_id > 1;

 

已選擇59622行。

 

 

執行計劃

----------------------------------------------------------

Plan hash value: 3227361085

 

--------------------------------------------------------------------------------

 

----------------

 

| Id  | Operation                   | Name             | Rows  | Bytes | Cost (%

 

CPU)| Time     |

 

--------------------------------------------------------------------------------

 

----------------

 

|   0 | SELECT STATEMENT            |                  | 59622 |  4890K|  1162

 (1)| 00:00:14 |

 

|   1 |  TABLE ACCESS BY INDEX ROWID| T_TEST_NOTEQU1   | 59622 |  4890K|  1162

 (1)| 00:00:14 |

 

|*  2 |   INDEX RANGE SCAN          | IDX_TEST_NOTEQU1 | 59622 |       |   341

 (1)| 00:00:05 |

 

--------------------------------------------------------------------------------

 

----------------

 

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   2 - access("A"."OBJECT_ID">1)

 

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

       2339  consistent gets

          0  physical reads

          0  redo size

    5836831  bytes sent via SQL*Net to client

       6941  bytes received via SQL*Net from client

        598  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      59622  rows processed

 

SQL>

而:

SQL> select (59622/100+384)*2 + 384 from dual;

 

(59622/100+384)*2+384

---------------------

              2344.44

 

當然這些測試僅僅在單例項,單個session,只對select來分析的,實際上加上了其他情況要複雜一些,但也還是有規律可循的。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16179598/viewspace-668279/,如需轉載,請註明出處,否則將追究法律責任。

相關文章