實戰:上億資料如何秒查
最近在忙著優化集團公司的乙個報表。優化完成後,報表查詢速度有從半小時以上(甚至查不出)到秒查的質變。從修改sql查詢語句邏輯到決定建立儲存過程實現,花了我3天多的時間,在此總結一下,希望對朋友們有幫助。
資料背景
首先專案是西門子中國在我司實施部署的mes專案,由於專案是在產線上運作(3 years+),資料累積很大。在專案的資料庫中,大概上億條資料的表有5個以上,千萬級資料的表10個以上,百萬級資料的表,很多…
(歷史問題,當初實施無人監管,無人監控資料庫這塊的效能問題。ps:我剛入職不久…)
不多說,直接貼西門子中國的開發人員在我司開發的ssrs報表中的sql語句:
select distinct b.materialid as matl_def_id, c.descript, case when right(b.mesorderid, 12) < 『001000000000』 then right(b.mesorderid, 9)
else right(b.mesorderid, 12) end as pom_order_id, a.lotname, a.sourcelotname as comlot,
e.defid as commaterials, e.descript as commatdes, d.vendorid, d.datecode,d.snnote, b.onplantid,a.sncust
from
(select m.lotname, m.sourcelotname, m.opetypeid, m.operationdate,n.sncust from view1 m
left join co_sn_link_customer as n on n.snmes=m.lotname
where
( m.lotname in (select val from fn_string_to_table(@sn,』,』,1)) or (@sn) = 『』) and
( m.sourcelotname in (select val from fn_string_to_table(@batchid,』,』,1)) or (@batchid) = 『』)
and (n.sncust like 『%』+ @sn_ext + 『%』 or (@sn_ext)=』』)
) aleft join
(select * from table1 where sntype = 『intsn』
and snrulename = 『productsnrule』
and onplantid=@onplant
) b on b.sn = a.lotname
inner join mmdefinitions as c on c.defid = b.materialid
left join table1 as d on d.sn = a.sourcelotname
inner join mmdefinitions as e on e.defid = d.materialid
where not exists (
select distinct lotname, sourcelotname from elcv_assemble_ops
where lotname = a.sourcelotname and sourcelotname = a.lotname
)and (d.datecode in (select val from fn_string_to_table(@dcode,』,』,1)) or (@dcode) = 『』)
and (d.snnote like 『%』+@snnote+』%』 or (@snnote) = 『』)
and ((case when right(b.mesorderid, 12) < 『001000000000』 then right(b.mesorderid, 9)
else right(b.mesorderid, 12) end) in (select val from fn_string_to_table(@order_id,』,』,1)) or (@order_id) = 『』)
and (e.defid in (select val from fn_string_to_table(@comdef,』,』,1)) or (@comdef) = 『』)
–view1是乙個巢狀兩層的檢視(出於保密性,實際名稱可能不同),裡面有一張上億資料的表和幾張千萬級資料的表做左連線查詢
–table1是乙個資料記錄超過1500萬的表
這個查詢語句,實際上通過我的檢測和調查,在b/s系統前端已無法查出結果,半小時,一小時 … 。因為我直接在sql查詢分析器查,半小時都沒有結果。
(原因是裡面對一張上億級資料表和3張千萬級資料表做全表掃瞄查詢)
不由感慨,西門子中國的素質(或者說責任感)就這樣?
下面說說我的分析和走的彎路(思維誤區),希望對你也有警醒。
探索和誤區
首先相關表的索引,沒有建全的,把索引給建上。
索引這步完成後,發現情況還是一樣,查詢速度幾乎沒有改善。後來想起相關千萬級資料以上的表,都還沒有建立表分割槽。於是考慮建立表分割槽以及資料複製的方案。
這裡有必要說明下:我司報表用的是乙個專門的資料庫伺服器,資料從產線訂閱而來。就是常說的「讀寫分離」。
如果直接在原表上建立表分割槽,你會發現執行表分割槽的事物會直接死鎖。原因是:表分割槽操作本身會鎖表,產線還在推資料過來,這樣很容易「阻塞」,「死鎖」。
我想好的方案是:建立乙個新錶(空表),在新表上建好表分割槽,然後複製資料過來。
正打算這麼幹。等等!我好像進入了乙個嚴重的誤區!
分析:原sql語句和業務需求,是對產線的資料做產品以及序列號的追溯,關鍵是查詢條件裡沒有有規律的」條件」(如日期、編號),
貿然做了表分割槽,在這裡幾乎沒有意義!反而會降低查詢效能!
一. 對原sql語句的分析
查詢語句的where條件,有大量@var in … or (@var =」) 的片段
where條件有like 『%』+@var+』%』
where條件有 case … end 函式
多次連線同一表查詢,另外使用本身已巢狀的檢視表,是不是必須,是否可替代?
sql語句有號,檢視中也有號出現
二. 優化設計
首先是用儲存過程改寫,好處是設計靈活。
核心思想是:用乙個或多個查詢條件(查詢條件要求至少輸入乙個)得到臨時表,每個查詢條件如果查到集合,就更新這張臨時表,最後彙總的時候,只需判斷這個臨時表是否有值。以此類推,可以建立多個臨時表,將查詢條件彙總。
這樣做目前來看至少兩點好處:
省去了對變數進行 =@var or (@var=」)的判斷;
拋棄sql拼接,提高**可讀性。
再有就是在書寫儲存過程,這個過程中要注意:
盡量想辦法使用臨時表掃瞄替代全表掃瞄;
拋棄in和not in語句,使用exists和not exists替代;
和客戶確認,模糊查詢是否有必要,如沒有必要,去掉like語句;
注意建立適當的,符合場景的索引;
踩死 「*」 號;
避免在where條件中對字段進行函式操作;
對實時性要求不高的報表,允許髒讀(with(nolock))。
三. 儲存過程
雖然犧牲了**的可讀性,但創造了效能價值。本人水平有限,還請各位不吝賜教!
最後,將ssrs報表替換成此儲存過程後,sql查詢分析器是秒查的。b/s前端用時1~2秒!
四. 總結
平常的你是否偶爾會因急於完成任務而書寫一堆效能極低的sql語句呢?寫出可靠性能的sql語句不難,難的是習慣。
耗時3天,上億資料如何做到秒級查詢?
最近在忙著優化集團公司的乙個報表。優化完成後,報表查詢速度由從半小時以上 甚至查不出 到秒查的質變。從修改 sql 查詢語句邏輯到決定建立儲存過程實現,花了我 3 天多的時間,在此總結一下,希望對朋友們有幫助。資料背景 首先專案是西門子中國在我司實施部署的 mes 專案,由於專案是在產線上運作 3 ...
高效率批量插入上億資料
轉至 create table create table tmp test chas lee f01 varchar2 20 f02 number 10 not null,f03 varchar2 21 f04 varchar2 21 f05 number,f06 number 20 建立乙個臨時表...
如何遍歷資料夾下上億檔案而不棧溢位
序 乙個資料夾下面有很多層的小檔案,如何算出這個資料夾下面有多少檔案?遞迴遍歷,簡單暴力,遞迴在一般情況確實是比較方便的解決方案,但是當資料夾深度多深,遞迴的反覆呼叫會導致方法一直無法釋放,造成jvm的棧溢位。那我們該怎麼辦?原文和作者一起討論 說實話這個問題我以前也沒有遇到過,我是聽一位我很敬佩的...