oracle 邏輯讀 物理讀

baojian322 13年前發布 | 3K 次閱讀 eCromedos OpenRelativity
1.物理讀(physical read)
當數據塊第一次讀取到,就會緩存到buffer cache 中,而第二次讀取和修改該數據塊時就在內存buffer cache
1.1  第一次讀取
SQL> set autotrace traceonly;
SQL> create table test_read as select * from dba_objects;
Table created.
 
SQL> select * from test_read;
 
50349 rows selected.
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4187308185
 
-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           | 53569 |  9259K|   157   (3)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| TEST_READ | 53569 |  9259K|   157   (3)| 00:00:02 |
-------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement
 
 
Statistics
----------------------------------------------------------
        288  recursive calls
          0  db block gets
       4120  consistent gets
        691  physical reads
          0  redo size
    5404806  bytes sent via SQL*Net to client
      37301  bytes received via SQL*Net from client
       3358  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      50349  rows processed

1.2  第二次讀取

SQL> select * from test_read;
 
50349 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4187308185
 
-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           | 53569 |  9259K|   157   (3)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| TEST_READ | 53569 |  9259K|   157   (3)| 00:00:02 |
-------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       4019  consistent gets
          0  physical reads
          0  redo size
    5404806  bytes sent via SQL*Net to client
      37301  bytes received via SQL*Net from client
       3358  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      50349  rows processed
 
SQL>

1.3  數據塊被重新讀入buffer cache

如果有新的數據需要被讀入Buffer Cache中,而Buffer Cache又沒有足夠的空閑空間,Oracle就根據LRU算法將LRU鏈表中LRU端的數據置換出去。
當這些數據被再次訪問到時,需要重新從磁盤讀入。 

SQL> alter session set events 'immediate trace name flush_cache';
 
Session altered.
 
SQL> select * from test_read;
 
50349 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4187308185
 
-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           | 53569 |  9259K|   157   (3)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| TEST_READ | 53569 |  9259K|   157   (3)| 00:00:02 |
-------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       4019  consistent gets
        692  physical reads
          0  redo size
    5404806  bytes sent via SQL*Net to client
      37301  bytes received via SQL*Net from client
       3358  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      50349  rows processed
 
SQL> 

2.邏輯讀(buffer read)

邏輯讀指的就是從(或者視圖從)Buffer Cache中讀取數據塊。按照訪問數據塊的模式不同,可以分為即時讀(Current Read)和一致性讀(Consistent Read)。
注意:邏輯IO只有邏輯讀,沒有邏輯寫。

(1)、即時讀(db block gets)
即時讀即讀取數據塊當前的最新數據。任何時候在Buffer Cache中都只有一份當前數據塊。即時讀通常發生在對數據進行修改、刪除操作時。
這時,進程會給數據加上行級鎖,并且標識數據為“臟”數據。

SQL> select * from test_read for update;                       
 
50349 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 428907831
 
--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           | 53569 |  9259K|   157   (3)| 00:00:02 |
|   1 |  FOR UPDATE        |           |       |       |            |          |
|   2 |   TABLE ACCESS FULL| TEST_READ | 53569 |  9259K|   157   (3)| 00:00:02 |
--------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement
 
 
Statistics
----------------------------------------------------------
        215  recursive calls
      51449  db block gets
       4894  consistent gets
          6  physical reads
   10505524  redo size
    4507795  bytes sent via SQL*Net to client
      37301  bytes received via SQL*Net from client
       3358  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      50349  rows processed
 
SQL> 

(2)、一致性讀(consistent gets)

Oracle是一個多用戶系統。當一個會話開始讀取數據還未結束讀取之前,可能會有其他會話修改它將要讀取的數據。
如果會話讀取到修改后的數據,就會造成數據的不一致。一致性讀就是為了保證數據的一致性。在Buffer Cache中的數據塊上都會有最后一次修改數據塊時的SCN。
如果一個事務需要修改數據塊中數據,會先在回滾段中保存一份修改前數據和SCN的數據塊,然后再更新Buffer Cache中的數據塊的數據及其SCN,并標識其為“臟”數據。
當其他進程讀取數據塊時,會先比較數據塊上的SCN和自己的SCN。如果數據塊上的SCN小于等于進程本身的SCN,則直接讀取數據塊上的數據;
如果數據塊上的SCN大于進程本身的SCN,則會從回滾段中找出修改前的數據塊讀取數據。通常,普通查詢都是一致性讀。

下面這個例子幫助大家理解一下一致性讀:

a、第一個會話

SQL> update test_read set status='Y' where object_id=20;

b、第二個會話

SQL> set autotrace traceonly;
SQL> select * from test_read;
 
50349 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4187308185
 
-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           | 53569 |  9259K|   157   (3)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| TEST_READ | 53569 |  9259K|   157   (3)| 00:00:02 |
-------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       4020  consistent gets
          0  physical reads
          0  redo size
    5404802  bytes sent via SQL*Net to client
      37301  bytes received via SQL*Net from client
       3358  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      50349  rows processed
 
SQL> 

結果:明顯看到目前是:4020  consistent gets
而以前是 :4019  consistent gets
說明這多出的一個是要從回滾段中獲取的
3、簡單例子分析一下邏輯讀中的一致性讀和即時讀
  1. Case1:    
  2. HELLODBA.COM>set time on    
  3. 10:22:09 HELLODBA.COM>update t_test1 set SECONDARY='A' where object_id = -1;    
  4. 1 row updated.    
  5. 10:22:22 HELLODBA.COM>commit;    
  6. Commit complete.    
  7. Session 1:    
  8. 10:22:25 HELLODBA.COM>update t_test1 set SECONDARY='B' where  object_id = -1 and SECONDARY='B' and (select count(*) from t_test2 t1, t_test2 t2) > 0;    
  9. rows updated.    
  10. 10:23:15 HELLODBA.COM>    
  11. Session 2:    
  12. 10:22:37 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1;    
  13. 1 row updated.    
  14. 10:23:02 HELLODBA.COM>commit;    
  15. Commit complete.    
  16. 10:23:04 HELLODBA.COM>    
  17. Case2:    
  18. 10:25:38 HELLODBA.COM>update t_test1 set SECONDARY='A' where object_id = -1;    
  19. 1 row updated.    
  20. 10:25:48 HELLODBA.COM>commit;    
  21. Commit complete.    
  22. Session 1:    
  23. 10:26:05 HELLODBA.COM>update t_test1 set SECONDARY='B' where  object_id = -1 and SECONDARY='A' and (select count(*) from t_test2 t1, t_test2 t2) > 0;    
  24. rows updated.    
  25. 10:27:21 HELLODBA.COM>    
  26. Session 2:    
  27. 10:26:16 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1;    
  28. 1 row updated.    
  29. 10:26:41 HELLODBA.COM>commit;    
  30. Commit complete.    
  31. 10:26:42 HELLODBA.COM>   

如果你觀察得足夠仔細,你可以從上面2個例子看到一個有趣的現象:無論session 1是否命中到數據,it最終都沒有修改數據。其根本原因就是當前模式讀與一致性讀的區別。

我們知道,為了減少并發沖突,Oracle引入了MVCC(多版本并發控制,也叫MCC)方法。在這種機制中,并發事務不會因為一致性的原因而相互阻塞,除非他們要修改同一條記錄。他們會將日志中所有SCN大于本身事務SCN的日志做回滾,以保證本事務讀取到的數據塊與事務SCN的一致。在Oracle中,這樣的讀取行為就稱為一致性讀。

然而,一致性讀所讀取到數據塊僅僅是某個時間點的一個快照,也就是說這樣的數據是只讀的。如果要修改數據,那么oracle需要讀取到當前的數據塊,也就是當前模式讀。

在一個UPDATE過程中,oracle會先一致性讀取與事務SCN一致的數據快照,并用where條件進行過濾。讓后根據讀取到數據塊的ID,再從當前數據中讀取到相應的數據塊進行修改。但是,如在事務啟動后到數據塊被讀取之間的這段時間內,相應的數據塊發生了改變,那么可能就會有我們意想不到的事情發生。

往回看我們的第一個例子。我們在session 1中,在10:22:25啟動了update事務。但是,由于該事務中存在一個大的子查詢,它會在幾十秒后才會讀取到需要被修改的數據。在Session 2中,我們在10:22:37開始update這些數據并在10:23:02提交了事務。而這個時間是早于數據在session 1中被讀取到的時間的。當session 2中的數據改變被提交后,session 1中的事務讀取到了該數據塊。因為session 2中的事務SCN大于session 1中的事務SCN,因此會讀取UNDO中的數據進行回滾,也就是說它讀取到數據SECONDARY是'A',再通過條件(SECONDARY='B')過濾后,沒有數據被命中,因此也沒有數據被修改。

在第二個例子中,session 1的事務在一致性讀取到數據塊之前也發生了類似的事情。當它回滾了數據后,它一致性讀取到了滿足過濾條件(SECONDARY='A')的數據塊。此時,它需要通過該數據塊ID再到當前數據中讀取該數據塊。但是因為當前數據塊的內容已經被session 2中的事務所修改,它還是沒有能修改到數據。

我想,通過這兩個例子,讀者應該更容易理解到當前模式讀與一致性讀之間的區別。


 本文由用戶 baojian322 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!