全表掃描卻產生大量db file sequential read一例
開發人員在進行新系統上線前的資料校驗測試時,發現一條手工執行的SQL執行了超過1小時還沒有返回結果。SQL很簡單:
- SELECT *
- FROM MOBILE_call_1204_OLD
- WHERE BILLING_NBR = '189xxxxxxxx'
- AND START_DATE = TO_DATE('2012-4-9 21:55:42', 'yyyy-mm-dd hh24:mi:ss')
下面是這條SQL的真實的執行計劃:
- -------------------------------------------------------------
-
| Id | Operation | Name
- -------------------------------------------------------------
- | 0 | SELECT STATEMENT | | |
- | * 1 | TABLE ACCESS FULL | MOBILE_CALL_1204_OLD | 1 |
- -------------------------------------------------------------
-
Predicate Information (identified by
- ---------------------------------------------------
- 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的指令碼):
- SQL> @waitprof print 4038 e 1000000
- DistinctAvgtime
- SID STATE EVENT TimeTime ms Events ms/Event
- ------- ------- ------------------------- ------- ------------ ---------- ----------
- 4038 WAITING db file sequential read 99.61 4482.450 272 16.480
- 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語句產生的大量單塊讀,又是屬於什麼情況呢?我們來看看單塊讀更細節的情況:
- SQL> @waitprof print 4038 e1 200000
- % Total Total Event DistinctAvgtime
- SID STATE EVENT P1 TimeTime ms Events ms/Event
- ------- ------- ------------------------ ------------------ ------------ ---------- ----------
- 4038 WAITING db file sequential read file#= 353 30.63 581.923 35 16.626
- 4038 WAITING db file sequential read file#= 355 28.14 534.641 40 13.366
- 4038 WAITING db file sequential read file#= 354 20.52 389.909 24 16.246
- 4038 WAITING db file sequential read file#= 3 19.63 372.942 35 10.655
- 4038 WORKING On CPU / runqueue .66 12.616 133 .095
- 4038 WAITING db file sequential read file#= 293 .42 7.971 1 7.971
多次執行同樣的SQL,發現絕大部分的單塊讀發生在3、353-355這四個檔案上,我們來看看這4個檔案是什麼:
- SQL> select file_id,tablespace_name from dba_data_files where file_id in (355,3,353,354);
- FILE_ID TABLESPACE_NAME
- ---------- ------------------------------
- 3 UNDOTBS1
- 353 UNDOTBS1
- 354 UNDOTBS1
- 355 UNDOTBS1
原來是UNDO表空間。那麼另一個疑問就會來了,為什麼在UNDO上產生了如此之多的單塊讀?首先要肯定的是,這條簡單的查詢語句,是進行的一致性讀。那麼在進行一致性讀的過程中,會有兩個動作會涉及到讀UNDO塊,延遲塊清除和構建CR塊。下面我們用另一個指令碼來檢視會話當時的狀況:
- SQL> @snapper all,out 5 3 4038
- Sampling SID 4038 with interval 5 seconds, taking 3 snapshots...
- setting stats toall due option = all
- -- Session Snapper v3.52 by Tanel Poder @ E2SN ( http://tech.e2sn.com )
- -------------------------------------------------------------------------------------------------------------------------------------
- SID, USERNAME , TYPE, STATISTIC , HDELTA, HDELTA/SEC, %TIME, GRAPH
- -------------------------------------------------------------------------------------------------------------------------------------
- 4038, BILL_MY , STAT, session logical reads , 488, 97.6,
- 4038, BILL_MY , STAT, user I/O wait time , 429, 85.8,
- 4038, BILL_MY , STAT, non - idle wait time , 429, 85.8,
- 4038, BILL_MY , STAT, non - idle wait count , 377, 75.4,
- 4038, BILL_MY , STAT, physical read total IO requests , 377, 75.4,
- 4038, BILL_MY , STAT, physical read total bytes , 3.13M, 625.87k,
- 4038, BILL_MY , STAT, cell physical IO interconnect bytes , 3.13M, 625.87k,
- 4038, BILL_MY , STAT, consistent gets , 488, 97.6,
- 4038, BILL_MY , STAT, consistent gets from cache , 488, 97.6,
- 4038, BILL_MY , STAT, consistent gets from cache (fastpath) , 8, 1.6,
- 4038, BILL_MY , STAT, consistent gets - examination , 478, 95.6,
- 4038, BILL_MY , STAT, logical read bytes from cache , 4M, 799.54k,
- 4038, BILL_MY , STAT, physical reads , 382, 76.4
- 4038, BILL_MY , STAT, physical reads cache , 382, 76.4,
- 4038, BILL_MY , STAT, physical read IO requests , 377, 75.4,
- 4038, BILL_MY , STAT, physical read bytes , 3.13M, 625.87k,
- 4038, BILL_MY , STAT, db block changes , 9, 1.8,
- 4038, BILL_MY , STAT, consistent changes , 469, 93.8,
- 4038, BILL_MY , STAT, free buffer requested , 392, 78.4,
- 4038, BILL_MY , STAT, CR blocks created , 10, 2,
- 4038, BILL_MY , STAT, physical reads cache prefetch , 5, 1,
- 4038, BILL_MY , STAT, shared hash latch upgrades - no wait , 375, 75,
- 4038, BILL_MY , STAT, calls to kcmgas , 376, 75.2,
- 4038, BILL_MY , STAT, redo entries , 9, 1.8,
- 4038, BILL_MY , STAT, redo size , 648, 129.6,
- 4038, BILL_MY , STAT, redo subscn max counts , 9, 1.8,
- 4038, BILL_MY , STAT, file io wait time , 4.3M, 860.97k,
- 4038, BILL_MY , STAT, data blocks consistent reads - undo records applied , 476, 95.2,
- 4038, BILL_MY , STAT, rollbacks only - consistent read gets , 1, .2,
- 4038, BILL_MY , STAT, cleanouts and rollbacks - consistent read gets , 9, 1.8,
- 4038, BILL_MY , STAT, immediate (CR) block cleanout applications , 9, 1.8,
- 4038, BILL_MY , STAT, commit txn count during cleanout , 9, 1.8,
- 4038, BILL_MY , STAT, cleanout - number of ktugct calls , 9, 1.8,
- 4038, BILL_MY , STAT, table scan rows gotten , 492, 98.4,
- 4038, BILL_MY , STAT, table scan blocks gotten , 10, 2,
- 4038, BILL_MY , STAT, heap block compress , 72, 14.4,
- 4038, BILL_MY , TIME, DB CPU , 60.99ms, 12.2ms, 1.2%, |@ |
- 4038, BILL_MY , TIME, sql execute elapsed time , 4.35s, 869.12ms, 86.9%, |@@@@@@@@@ |
- 4038, BILL_MY , TIME, DB time , 4.35s, 869.12ms, 86.9%, |@@@@@@@@@ |
- 4038, BILL_MY , WAIT, db file sequential read , 4.17s, 834.69ms, 83.5%, |@@@@@@@@@ |
- 4038, BILL_MY , WAIT, db file scattered read , 49.17ms, 9.83ms, 1.0%, |@ |
- -- End of Stats snap 1, end=2012-05-21 22:22:16, seconds=5
- ...省略另兩次的取樣輸出。
上面的結果是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可能比較快就執行完了)。詢問發測試的人員,稱沒有什麼大事務執行過,耳聽為虛,眼見為實:
- SQL> select object_id from dba_objects where object_name='MOBILE_CALL_1204_OLD';
- OBJECT_ID
- ----------
- 75858
- SQL> select * from v$lock where id1=75858;
- norows selected
- SQL> select * from dba_tab_modifications where table_name=upper('MOBILE_call_1204_OLD');
- INSERTS UPDATES DELETES TIMESTAMP TRU DROP_SEGMENTS
- ---------- ---------- ---------- ------------------- --- -------------
- 0 162696447 0 2012-05-21 22:00:02 NO 0
這張表目前沒有事務,但是曾經update了超過1.6億條記錄。最後一次DML的時間正是這條執行很慢的SQL開始執行之後的時間(這裡不能說明最後一次事務量很大,也不能說明最後一次修改對SQL造成了很大影響,但是這裡證明了這張表最近的確是修改過,並不是像測試人員說的那樣沒有修改過)。
實際上對於這張表要做的操作,我之前是類似的表上是有看過的。這張表的總行數有上億條,而這張表由於進行資料的人工處理,需要update掉絕大部分的行,update時使用並行處理。那麼這個問題到,從時間順序上來講,應該如下:
- 在表上有很大的事務,但是還沒有提交。
- 問題SQL開始執行查詢。
- 事務提交。
- 在檢查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,要解決其速度慢的問題,有兩種方案:
- 在表上建個索引,如果類似的SQL還要多次執行,這是最佳方案。
- 取消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:通過讀物理表獲取資料