1. 程式人生 > >全表掃描卻產生大量db file sequential read一例

全表掃描卻產生大量db file sequential read一例

開發人員在進行新系統上線前的資料校驗測試時,發現一條手工執行的SQL執行了超過1小時還沒有返回結果。SQL很簡單:

  1. SELECT *  
  2.   FROM MOBILE_call_1204_OLD  
  3.  WHERE BILLING_NBR = '189xxxxxxxx'
  4.    AND START_DATE = TO_DATE('2012-4-9 21:55:42''yyyy-mm-dd hh24:mi:ss')  

下面是這條SQL的真實的執行計劃:

  1. -------------------------------------------------------------
  2. | Id   | Operation          | Name
                     | E-Rows |  
  3. -------------------------------------------------------------
  4. |    0 | SELECT STATEMENT   |                      |        |  
  5. | *  1 |  TABLE ACCESS FULL | MOBILE_CALL_1204_OLD |      1 |  
  6. -------------------------------------------------------------
  7. Predicate Information (identified by
     operation id):  
  8. ---------------------------------------------------
  9. 1 - filter(("START_DATE"=TO_DATE(' 2012-04-09 21:55:42''syyyy-mm-dd hh24:mi:ss'AND"BILLING_NBR"='189xxxxxxxx'))  

很顯然,在這個表上建billing_nbr和start_date的複合索引,這條SQL就能很快執行完(實際上最後也建了索引)。但是這裡我們要探討的是,為什麼這麼一條簡單的SQL語句,執行了超過1小時還沒有結果。MOBILE_CALL_1204_OLD這張表的大小約為12GB,以系統的IO能力,正常情況下不會執行這麼長的時間。簡單地看了一下,系統的CPU以及IO壓力都不高。假設單程序全表掃描表,每秒掃描50MB大小(這實際上是一個很保守的掃描速度了),那麼只需要245秒就可以完成掃描。

下面來診斷一下SQL為什麼會這麼不正常地慢。看看會話的等待(以下會用到Oracle大牛Tanel Poder的指令碼):

  1. SQL> @waitprof print 4038 e 1000000  
  2.                                                                   DistinctAvgtime
  3.     SID STATE   EVENT                         TimeTime ms     Events   ms/Event   
  4. ------- ------- -------------------------  ------- ------------ ---------- ----------
  5.    4038 WAITING db file sequential read      99.61     4482.450        272     16.480  
  6.    4038 WORKING On CPU / runqueue              .39       17.550        271       .065  

明明是全表掃描的SQL,為什麼99%以上的等待時間是db file sequential read,即單塊讀?!多執行幾次waitprof指令碼,得到的結果是一致的(注意這裡的資料,特別是平均等待時間並不一定是準確的值,這裡重點關注的是等待時間的分佈)。

那麼SQL執行計劃為全表掃描(或索引快速全掃描)的時候,在執行時會有哪些情況實際上是單塊讀?我目前能想到的有:

  • db_file_multiblock_read_count引數設定為1
  • 表或索引的大部分塊在buffer cache中,少量不連續的塊在磁碟上。
  • 一些特殊的塊,比如段頭
  • 行連結的塊
  • LOB列的索引塊和cache的LOB塊(雖然10046事件看不到lob索引和cache的lob的讀等待,但客觀上是存在的。)

那麼在這條SQL語句產生的大量單塊讀,又是屬於什麼情況呢?我們來看看單塊讀更細節的情況:

  1. SQL> @waitprof print 4038 e1 200000  
  2.                                                     % Total  Total Event   DistinctAvgtime
  3.     SID STATE   EVENT                    P1            TimeTime ms     Events   ms/Event  
  4. ------- ------- ------------------------ ------------------ ------------ ---------- ----------
  5.    4038 WAITING db file sequential read  file#= 353   30.63      581.923        35     16.626  
  6.    4038 WAITING db file sequential read  file#= 355   28.14      534.641        40     13.366  
  7.    4038 WAITING db file sequential read  file#= 354   20.52      389.909        24     16.246  
  8.    4038 WAITING db file sequential read  file#= 3     19.63      372.942        35     10.655  
  9.    4038 WORKING On CPU / runqueue                       .66       12.616       133       .095  
  10.    4038 WAITING db file sequential read  file#= 293     .42        7.971         1      7.971  

多次執行同樣的SQL,發現絕大部分的單塊讀發生在3、353-355這四個檔案上,我們來看看這4個檔案是什麼:

  1. SQL> select file_id,tablespace_name from dba_data_files where file_id in (355,3,353,354);  
  2.    FILE_ID TABLESPACE_NAME  
  3. ---------- ------------------------------
  4.          3 UNDOTBS1  
  5.        353 UNDOTBS1  
  6.        354 UNDOTBS1  
  7.        355 UNDOTBS1  

原來是UNDO表空間。那麼另一個疑問就會來了,為什麼在UNDO上產生了如此之多的單塊讀?首先要肯定的是,這條簡單的查詢語句,是進行的一致性讀。那麼在進行一致性讀的過程中,會有兩個動作會涉及到讀UNDO塊,延遲塊清除和構建CR塊。下面我們用另一個指令碼來檢視會話當時的狀況:

  1. SQL> @snapper all,out 5 3 4038    
  2. Sampling SID 4038 with interval 5 seconds, taking 3 snapshots...  
  3. setting stats toall due option = all
  4. -- Session Snapper v3.52 by Tanel Poder @ E2SN ( http://tech.e2sn.com )
  5. -------------------------------------------------------------------------------------------------------------------------------------
  6.     SID, USERNAME  , TYPE, STATISTIC                                                 ,     HDELTA, HDELTA/SEC,    %TIME, GRAPH  
  7. -------------------------------------------------------------------------------------------------------------------------------------
  8.    4038, BILL_MY   , STAT, session logical reads                                     ,        488,       97.6,  
  9.    4038, BILL_MY   , STAT, user I/O wait time                                        ,        429,       85.8,  
  10.    4038, BILL_MY   , STAT, non - idle wait time                                      ,        429,       85.8,  
  11.    4038, BILL_MY   , STAT, non - idle wait count                                     ,        377,       75.4,  
  12.    4038, BILL_MY   , STAT, physical read total IO requests                           ,        377,       75.4,  
  13.    4038, BILL_MY   , STAT, physical read total bytes                                 ,      3.13M,    625.87k,  
  14.    4038, BILL_MY   , STAT, cell physical IO interconnect bytes                       ,      3.13M,    625.87k,  
  15.    4038, BILL_MY   , STAT, consistent gets                                           ,        488,       97.6,  
  16.    4038, BILL_MY   , STAT, consistent gets from cache                                ,        488,       97.6,  
  17.    4038, BILL_MY   , STAT, consistent gets from cache (fastpath)                     ,          8,        1.6,  
  18.    4038, BILL_MY   , STAT, consistent gets - examination                             ,        478,       95.6,  
  19.    4038, BILL_MY   , STAT, logical read bytes from cache                             ,         4M,    799.54k,  
  20.    4038, BILL_MY   , STAT, physical reads                                            ,        382,       76.4  
  21.    4038, BILL_MY   , STAT, physical reads cache                                      ,        382,       76.4,  
  22.    4038, BILL_MY   , STAT, physical read IO requests                                 ,        377,       75.4,  
  23.    4038, BILL_MY   , STAT, physical read bytes                                       ,      3.13M,    625.87k,  
  24.    4038, BILL_MY   , STAT, db block changes                                          ,          9,        1.8,  
  25.    4038, BILL_MY   , STAT, consistent changes                                        ,        469,       93.8,  
  26.    4038, BILL_MY   , STAT, free buffer requested                                     ,        392,       78.4,  
  27.    4038, BILL_MY   , STAT, CR blocks created                                         ,         10,          2,  
  28.    4038, BILL_MY   , STAT, physical reads cache prefetch                             ,          5,          1,  
  29.    4038, BILL_MY   , STAT, shared hash latch upgrades - no wait                      ,        375,         75,  
  30.    4038, BILL_MY   , STAT, calls to kcmgas                                           ,        376,       75.2,  
  31.    4038, BILL_MY   , STAT, redo entries                                              ,          9,        1.8,  
  32.    4038, BILL_MY   , STAT, redo size                                                 ,        648,      129.6,  
  33.    4038, BILL_MY   , STAT, redo subscn max counts                                    ,          9,        1.8,  
  34.    4038, BILL_MY   , STAT, file io wait time                                         ,       4.3M,    860.97k,  
  35.    4038, BILL_MY   , STAT, data blocks consistent reads - undo records applied       ,        476,       95.2,  
  36.    4038, BILL_MY   , STAT, rollbacks only - consistent read gets                     ,          1,         .2,  
  37.    4038, BILL_MY   , STAT, cleanouts and rollbacks - consistent read gets            ,          9,        1.8,  
  38.    4038, BILL_MY   , STAT, immediate (CR) block cleanout applications                ,          9,        1.8,  
  39.    4038, BILL_MY   , STAT, commit txn count during cleanout                          ,          9,        1.8,  
  40.    4038, BILL_MY   , STAT, cleanout - number of ktugct calls                         ,          9,        1.8,  
  41.    4038, BILL_MY   , STAT, table scan rows gotten                                    ,        492,       98.4,  
  42.    4038, BILL_MY   , STAT, table scan blocks gotten                                  ,         10,          2,  
  43.    4038, BILL_MY   , STAT, heap block compress                                       ,         72,       14.4,  
  44.    4038, BILL_MY   , TIME, DB CPU                                                    ,    60.99ms,     12.2ms,     1.2%, |@         |  
  45.    4038, BILL_MY   , TIME, sql execute elapsed time                                  ,      4.35s,   869.12ms,    86.9%, |@@@@@@@@@ |  
  46.    4038, BILL_MY   , TIME, DB time                                                   ,      4.35s,   869.12ms,    86.9%, |@@@@@@@@@ |  
  47.    4038, BILL_MY   , WAIT, db file sequential read                                   ,      4.17s,   834.69ms,    83.5%, |@@@@@@@@@ |  
  48.    4038, BILL_MY   , WAIT, db file scattered read                                    ,    49.17ms,     9.83ms,     1.0%, |@         |  
  49. --  End of Stats snap 1, end=2012-05-21 22:22:16, seconds=5
  50. ...省略另兩次的取樣輸出。  

上面的結果是5秒左右的會話取樣資料。再一次提醒,涉及到時間,特別要精確到毫秒的,不一定很精確,我們主要是看資料之間的對比。從上面的資料來看,會話請求了382次IO請求,單塊讀和多塊讀一共耗時4219.17ms(4.17s+49.17ms),平均每次IO耗時11ms。這個單次IO速度對這套系統的要求來說相對較慢,但也不是慢得很離譜。data blocks consistent reads - undo records applied這個統計值表示進行一致性讀時,回滾的UNDO記錄條數。
比這個統計值可以很明顯地看出,這條SQL在執行時,為了得到一致性讀,產生了大量的UNDO記錄回滾。那麼很顯然,在這條SQL語句開始執行的時候,表上有很大的事務還沒有提交。當然還有另一種可能是SQL在執行之後有新的很大的事務(不過這種可能性較小一些,因為那樣的話這條SQL可能比較快就執行完了)。詢問發測試的人員,稱沒有什麼大事務執行過,耳聽為虛,眼見為實:

  1. SQL> select object_id from dba_objects where object_name='MOBILE_CALL_1204_OLD';  
  2.  OBJECT_ID  
  3. ----------
  4.      75858  
  5. SQL> select * from v$lock where id1=75858;  
  6. norows selected  
  7. SQL> select * from dba_tab_modifications where table_name=upper('MOBILE_call_1204_OLD');  
  8.    INSERTS    UPDATES    DELETES TIMESTAMP           TRU DROP_SEGMENTS  
  9. ---------- ---------- ---------- ------------------- --- -------------
  10.          0  162696447          0 2012-05-21 22:00:02 NO              0  

這張表目前沒有事務,但是曾經update了超過1.6億條記錄。最後一次DML的時間正是這條執行很慢的SQL開始執行之後的時間(這裡不能說明最後一次事務量很大,也不能說明最後一次修改對SQL造成了很大影響,但是這裡證明了這張表最近的確是修改過,並不是像測試人員說的那樣沒有修改過)。

實際上對於這張表要做的操作,我之前是類似的表上是有看過的。這張表的總行數有上億條,而這張表由於進行資料的人工處理,需要update掉絕大部分的行,update時使用並行處理。那麼這個問題到,從時間順序上來講,應該如下:

  1. 在表上有很大的事務,但是還沒有提交。
  2. 問題SQL開始執行查詢。
  3. 事務提交。
  4. 在檢查SQL效能問題時,表上已經沒有事務。

由於update量很大,那麼UNDO佔用的空間也很大,但是可能由於其他活動的影響,很多UNDO塊已經刷出記憶體,這樣在問題SQL執行時,大量的塊需要將塊回滾到之前的狀態(雖然事務開始於查詢SQL,但是是在查詢SQL開始之後才提交的,一致性讀的SCN比較是根據SQL開始的SCN與事務提交SCN比較的,而不是跟事務的開始SCN比較),這樣需要訪問到大量的UNDO塊,但是UNDO塊很多已經不在記憶體中,就不得不從磁碟讀入。

對於大事務,特別是更新或DELETE數千萬記錄的大事務,在生產系統上儘量避免單條SQL一次性做。這造成的影響特別大,比如:

  • 事務可能意外中斷,回滾時間很長,事務恢復時過高的並行度可能引起負載增加。
  • 表中大量的行長時間被鎖住。
  • 如果事務意外中斷,長時間的回滾(恢復)過程中,可能嚴重影響SQL效能(因為查詢時需要回滾塊)。
  • 事務還未提交時,影響SQL效能,比如本文中提到的情況。
  • 消耗過多UNDO空間。
  • 對於DELETE大事務,有些版本的oracle在空閒空間查詢上會有問題,導致在INSERT資料時,查詢空間導致過長的時間。
  • 對於RAC資料庫,由於一致性讀的代價更大,所以大事務的危害更大。

那麼,現在我們可以知道,全表掃描過程還會產生單塊讀的情況有,讀UNDO塊。
對於這條SQL,要解決其速度慢的問題,有兩種方案:

  1. 在表上建個索引,如果類似的SQL還要多次執行,這是最佳方案。
  2. 取消SQL,重新執行。因為已經沒有事務在執行,重新執行只是會產生事務清除,但不會回滾UNDO記錄來構建一致性讀塊。

繼續回到問題,從統計資料來看,每秒只構建了少量的一致性讀塊(CR block created,table scan blocks gotten這兩個值均為2),每秒的table scan rows gotten值為98.4,通過dump資料塊可以發現塊上的行數基本上在49行左右,所以一致性讀塊數和行數是匹配的。session logical reads每秒為97.6,由於每回滾一條undo記錄都要記錄一次邏輯讀,這個值跟每秒獲取的行數也是匹配的(誤差值很小),與data blocks consistent reads - undo records applied的值也是很接近的。問題到這兒,產生了一個疑問,就是單塊讀較多(超過70),因此可以推測,平均每個undo塊只回滾了不到2條的undo記錄,同時同一資料塊上各行對應的undo記錄很分散,分散到了多個undo塊中,通常應該是聚集在同一個塊或相鄰塊中,這一點非常奇怪,不過現在已經沒有這個環境(undo塊已經被其他事務重用),不能繼續深入地分析這個問題,就留著一個疑問,歡迎探討(一個可能的解釋是塊是由多個併發事務修改的,對於這個案例,不會是這種情況,因為在資料塊的dump中沒有過多ITL,另外更不太可能是一個塊更新了多次,因為表實在很大,在短時間內不可能在表上發生很多次這樣的大事務)。

在最後,我特別要提到,在生產系統上,特別是OLTP型別的系統上,儘量避免大事務。

相關推薦

掃描產生大量db file sequential read

開發人員在進行新系統上線前的資料校驗測試時,發現一條手工執行的SQL執行了超過1小時還沒有返回結果。SQL很簡單: SELECT *     FROM MOBILE_call_1204_OLD    WHERE BILLING_NBR = '189xxxxxxxx'

避免掃描的sql優化

設計 結束 edate bstr 需要 表達 大量數據 第一個 關鍵字 摘抄自:http://www.cnblogs.com/jameslif/p/6406167.html 對查詢進行優化,應盡量避免全表掃描,首先應考慮在where 及order by 涉及的列上建立索引

ORACLE sql調優之記錄次trim函數引發的大掃描

oracle trim 全表掃描 sql 調優 2017年8月14日,一地市oracle相關的調度程序ETL抽取速度奇慢,sql語句每次執行平均時間要9秒左右,如果所示:該調度過程涉及的sql語句如下:select count(*) from (SELECT rtrim(

項目owner看這裏,MaxCompute掃描新功能,給你“失誤”的機會

業務需求 機會 表數據 人的 人員 做了 設置 cli ssi 摘要: MaxCompute發布了“ALIAS 命令”,提供了在不修改代碼的前提下,在MapReduce或自定義函數(UDF) 代碼中,通過某個固定的資源名讀取不同資源(數據)的需求。隨著社會數據收集手段的不斷

造成MySQL掃描的原因

記錄 添加 its 工程師 review 全表掃描 字段 count 查詢條件 全表掃描是數據庫搜尋表的每一條記錄的過程,直到所有符合給定條件的記錄返回為止。通常在數據庫中,對無索引的表進行查詢一般稱為全表掃描;然而有時候我們即便添加了索引,但當我們的SQL語句寫的不合理的

關係型資料庫掃描分片詳解

導讀:資料匯流排(DBus)專注於資料的實時採集與實時分發,可以對IT系統在業務流程中產生的資料進行匯聚,經過轉換處理後成為統一JSON的資料格式(UMS),提供給不同資料使用方訂閱和消費,充當數倉平臺、大資料分析平臺、實時報表和實時營銷等業務的資料來源。在上一篇關於DBus的文章中,我們主要介紹了在DBus

Oracle 檢查資料庫有哪些頻繁進行掃描

select a.object_name, a.sql_id, b.sql_text, max(b.executions) executions, max(b.last_active_time) last_active_time, b.first_load_time from v$sql_plan a,

MyBatis實戰之對映器 SSM框架之批量增加示例(同步請求jsp檢視解析) mybatis的批量更新例項 造成MySQL掃描的原因 SSM框架實戰之整合EhCache

對映器是MyBatis最強大的工具,也是我們使用MyBatis時用得最多的工具,因此熟練掌握它十分必要。MyBatis是針對對映器構造的SQL構建的輕量級框架,並且通過配置生成對應的JavaBean返回給呼叫者,而這些配置主要便是對映器,在MyBatis中你可以根據情況定義動態SQL來滿足不同場景的需要,它比

Mysql避免掃描的sql查詢優化

對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引:  嘗試下面的技巧以避免優化器錯選了表掃描:   使用ANALYZE TABLE tbl_

scala操作Hbas -掃描

package com.blm.util import javax.ws.rs.core.Response.Status.Family import org.apache.hadoop.hbase.{CellUtil, HBaseConfiguration, TableName} im

如何對10億資料量級的mongoDB作高效的掃描

本文連結: http://quentinXXZ.iteye.com/blog/2149440 一、正常情況下,不應該有這種需求 首先,大家應該有個概念,標題中的這個問題,在大多情況下是一個偽命題,不應該被提出來。要知道,對於一般較大資料量的資料庫,全表查詢,這種操作一般情況下是不應該出現的,在做正常查

Oracle 11g掃描以Direct Path Read方式執行

在Oracle Database 11g中有一個新特性,全表掃描可以通過直接路徑讀的方式來執行(Direct Path Read),這是一個合理的變化,如果全表掃描的大量資料讀取是偶發性的,則直接路徑讀可以避免大量資料對於Buffer Cache的衝擊。當然對於小表來說,Oracle允許通過Buffer C

MySQL查詢優化之避免掃描

原文地址:https://dev.mysql.com/doc/refman/5.7/en/table-scan-avoidance.html 譯文: 8.2.1.20 避免全表掃描 當MySQL使用全表掃描來解析查詢時,EXPLAIN的輸出結果中將在type列顯示ALL。這種情況通常發生

索引 vs 掃描

之前我們介紹了第一個檔案格式: 在這個檔案格式裡,資料沒有排序,順序儲存,我們只提供了查詢所有資料的介面,當我們想進行值過濾時,比如查詢大於10的資料,需要將所有資料遍歷一遍,如果把這個檔案看做一個只有一列的表,這種查詢方式就叫全表掃描。 磁碟結構和

HBase 帶有過濾條件的掃描 小計

 HBase 帶有過濾條件的全表掃描     字首過濾器     列名過濾器     時間戳過濾器     組合過濾器 scan 'demo_table' , {     FILTER

表裡有索引,為什麼還都是掃描

這是有CBO根據執行計劃的成本決定的 exec dbms_stats.gather_table_stats(ownname='test',tabname=>'dept',cascade=>true); 用/*+ index(table_name index_

高水位線和掃描

   高水位線好比水庫中儲水的水位線,用於描述資料庫中段的擴充套件方式。高水位線對全表掃描方式有著至關重要的影響。當使用delete 操作 表記錄時,高水位線並不會下降,隨之導致的是全表掃描的實際開銷並沒有任何減少。本文給出高水位線的描述,如何降低高水位線,以及高水 位線對

Mysql怎麼樣避免掃描,sql查詢優化

對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引:  嘗試下面的技巧以避免優化器錯選了表掃描: 使用ANALYZE TABLE tbl_name為掃描的表更新關鍵字分佈。 對掃描的表使用FORCE INDEX告知My

Oracle SQL優化必要的掃描思路分析

大多數情況下,我們需要避免SQL在查詢時進行全表掃描(FTS),但是對於必須需要進行全表掃描的情況,也可以進行一些優化處理。 即使全表掃描是檢索所需資料的唯一可行方法,仍然有多種方法來提升查詢效能。優化掃描的技術包括: 1、  通過讓表變小來減少掃描所需的邏輯塊讀取的數量

mysql 掃描索引掃描、索引覆蓋(覆蓋索引)

full index scan:全索引掃描,查詢時,遍歷索引樹來獲取資料行。如果資料不是密集的會產生隨機IO 在執行計劃中是Type列,index full table scan:通過讀物理表獲取資料