您好,登錄后才能下訂單哦!
基于數(shù)據(jù)庫系統(tǒng)的 T+0 全量實時查詢,在數(shù)據(jù)量很大時一般只能進(jìn)行數(shù)據(jù)庫擴(kuò)容(包括分庫手段),成本高昂;如果采用文件系統(tǒng)和生產(chǎn)數(shù)據(jù)庫混合運算,就可以實現(xiàn)低成本高性能的 T+0 查詢,而熱導(dǎo)出機(jī)制則是這個方案的基礎(chǔ)!
在報表的應(yīng)用系統(tǒng)中,用戶越來越關(guān)注數(shù)據(jù)的實時性,希望最新發(fā)生的數(shù)據(jù)能在報表中體現(xiàn)出來,也就是我們常說的T+0場景, 以此及時輔助決策、驅(qū)動運營。
比如交通大數(shù)據(jù)應(yīng)用的場景:需要結(jié)合實時數(shù)據(jù)了解車輛通行密度,合理進(jìn)行道路規(guī)劃,同時根據(jù)歷史數(shù)據(jù)預(yù)測線路擁堵情況、事故多發(fā)地提醒等等。
但常規(guī)的方案:報表+數(shù)據(jù)倉庫+ETL工具很難實現(xiàn)此類實時報表,往往只能看到昨天、上周甚至是上個月的情況,也就是T+1、T+7、T+30等,我們統(tǒng)稱為T+n報表。
究其原因,困難大概體現(xiàn)在如下三個方面:
1、如果報表的歷史數(shù)據(jù)和最新數(shù)據(jù)都從生產(chǎn)系統(tǒng)讀取,雖然可以實現(xiàn)T+0報表,但是會對生產(chǎn)數(shù)據(jù)庫造成壓力,當(dāng)數(shù)據(jù)量越來越大時,產(chǎn)生性能瓶頸,直接影響業(yè)務(wù);并且大量的歷史數(shù)據(jù)會占用高昂的數(shù)據(jù)庫成本(存儲成本和性能成本)。
2、如果采用數(shù)據(jù)倉庫的方式,那么ETL從生產(chǎn)庫中取出數(shù)據(jù),需要較長的“窗口時間”,一般是業(yè)務(wù)人員下班之后,到第二天早上上班之前,所以能看到的最新數(shù)據(jù)也只能是T+1。
3、雖然理論上可以從歷史庫中和生產(chǎn)庫中同時取數(shù)據(jù)形成實時報表,但是一般的報表工具都不具備跨庫混合計算的能力,其他的跨庫計算方案又比較復(fù)雜,難以實施,并且性能較低。
那么,是否有成本更低、實施起來更簡單的T+0報表方案呢?下面將要介紹的潤乾集算器,就是這樣一款利器,利用集算器的混合數(shù)據(jù)源能力就能實現(xiàn)低成本的T+0實時報表。
實現(xiàn)思路:把不再發(fā)生變動的大量歷史數(shù)據(jù)采用數(shù)據(jù)文件存儲,僅從生產(chǎn)庫讀取少量新數(shù)據(jù),在保證報表實時性的同時,降低了歷史數(shù)據(jù)存儲的成本,減少了報表系統(tǒng)對生產(chǎn)數(shù)據(jù)庫造成的負(fù)載。
下圖顯示了常規(guī)T+n方案和集算器T+0方案的結(jié)構(gòu)對比,應(yīng)該說,引入集算器后,減少了很多不必要的成本和多余的組件,整個體系架構(gòu)也變得更加清新與合理了:
上圖新處理方式體系結(jié)構(gòu)中的”導(dǎo)出(非實時)”是指在非工作時間(例如晚上),定時將生產(chǎn)數(shù)據(jù)庫的新增數(shù)據(jù)同步到存儲歷史數(shù)據(jù)的文件中;
關(guān)于數(shù)據(jù)外置方案、設(shè)計數(shù)據(jù)存儲組織、定時任務(wù)等相關(guān)準(zhǔn)備和外圍工作,具體做法可參考<<基于文件系統(tǒng)實現(xiàn)可追加的數(shù)據(jù)集市>>的相關(guān)章節(jié),這里不再贅述。
下面,我們就通過制作“實時流程工站不良柏拉圖”這個例子,來看一下集算器是如何利用歷史數(shù)據(jù)結(jié)合當(dāng)期數(shù)據(jù)進(jìn)行混合運算,實現(xiàn)T+0方案的。報表最終的展示效果如下圖:
這張報表清楚地顯示了電子設(shè)備在生產(chǎn)過程中,80%的問題是由20%的原因造成的,對于找出產(chǎn)生大多數(shù)問題的關(guān)鍵原因很有優(yōu)勢。
報表中數(shù)據(jù)的查詢過程是:根據(jù)選擇開始日期、結(jié)束日期進(jìn)行過濾查詢;先按照不良代碼分組,統(tǒng)計匯總每個分類的不良數(shù)量,并按照匯總數(shù)量降序,然后計算出不良累計比率(算法為“(不良數(shù)量累計匯總/總不良數(shù)量匯總)*100”)。報表上部的查詢按鈕是報表工具提供的“參數(shù)模板”功能,具體做法參見教程,這里不再贅述。
我們假定已經(jīng)將變化不大的歷史數(shù)據(jù)搬出了數(shù)據(jù)庫,采用集文件(集文件利用集算器提供的壓縮格式,具有更好IO性能)存儲,命名為MES-pre.btx,同時每天定時執(zhí)行數(shù)據(jù)同步腳本,把前一天的數(shù)據(jù)追加到當(dāng)前數(shù)據(jù)文件中;查詢涉及的當(dāng)天少量數(shù)據(jù)直接從生產(chǎn)數(shù)據(jù)庫(demo)取出,以此保證數(shù)據(jù)的實時性。集算器SPL腳本如下(也支持僅查歷史數(shù)據(jù)的情況):
A | B | |
1 | =connect("demo") | =A1.query("SELECT code,name FROM watch") |
2 | =A1.cursor@x("SELECT code,nums FROM meta_resource WHERE "+if(Efiledate>=date(now()),"DATE_FORMAT(fildate,'%Y-%m-%d')>='"+string(date(now()))+"'","1=0")) | |
3 | =file("D:/PT/MES-pre.btx").cursor@b().select(filedate>=Bfiledate && filedate<Efiledate) | =[A2,A3].conjx() |
4 | =B3.groups(code:不良代碼,code:不良名稱;sum(nums):不良數(shù)量,sum(nums):不良累計數(shù)量,sum(0):不良累計比率) | |
5 | >A4.switch(不良名稱,B1:code) | =A4.sort(不良數(shù)量:-1) |
6 | >B5.run(不良累計數(shù)量+=不良累計數(shù)量[-1]) | =A4.sum(不良數(shù)量) |
7 | >B5.run(不良累計比率=round(不良累計數(shù)量/B6*100,2)) | =B5.new(不良代碼,不良名稱.name:不良名稱,不良數(shù)量,不良累計數(shù)量,不良累計比率) |
8 | return B7 |
A1:連接預(yù)先配置好的生產(chǎn)數(shù)據(jù)庫(demo)
B1:查詢字典表,不良代碼、不良名稱
A2:建立數(shù)據(jù)庫游標(biāo),用簡單的sql讀取數(shù)據(jù)表的數(shù)據(jù)。Sql的過濾條件部分會根據(jù)邏輯判斷進(jìn)行動態(tài)拼接,當(dāng)結(jié)束日期>=當(dāng)前系統(tǒng)日期時,代表查詢當(dāng)天的實時數(shù)據(jù),否則做一次結(jié)果為空的查詢動作,以適應(yīng)只查歷史數(shù)據(jù)的業(yè)務(wù)場景。@x選項是指讀完數(shù)據(jù)庫后關(guān)閉連接。
A3:建立數(shù)據(jù)文件D:/PT/MES-pre.btx的游標(biāo)。文件游標(biāo)允許分批從大數(shù)據(jù)文件中讀取數(shù)據(jù),從而避免內(nèi)存溢出。@b選項是指按照集算器提供的二進(jìn)制格式來讀取文件,同時根據(jù)傳入的開始日期(Bfiledate)、結(jié)束日期(Efiledate)過濾出符合條件的記錄
B3:將數(shù)據(jù)庫游標(biāo)(新數(shù)據(jù))和文件游標(biāo)(歷史數(shù)據(jù))合并
A4:利用groups函數(shù),完成對合并后游標(biāo)的分組匯總,同時多構(gòu)造了幾列:不良名稱、不良累計數(shù)量、不良累計比率,方便后面的賦值計算。
A5:通過switch()函數(shù)在A4結(jié)果的”不良名稱”字段上建立指向B1表中code字段的指針引用記錄,實現(xiàn)關(guān)聯(lián),如下圖:
B5:按照不良數(shù)量降序排列。如下圖:
A6:計算不良累計數(shù)量;可以看到,集算器用“不良累計數(shù)量[-1]”來表示上一行的不良數(shù)量,可以輕松進(jìn)行相對位置的計算。
B6:對不良數(shù)量進(jìn)行總計
A7:計算出不良累計比率,算法為“(不良數(shù)量累計匯總/總不良數(shù)量匯總)*100”,同時保留兩位小數(shù),計算結(jié)果如下圖:
B7-A8:取出需要的字段,將關(guān)聯(lián)了不良名稱后的結(jié)果集返回給報表工具,如下圖:
在利用集算器完成了數(shù)據(jù)查詢工作后,為了在報表中使用查詢結(jié)果,可以在報表中直接將集算器設(shè)置為數(shù)據(jù)源,用法和使用數(shù)據(jù)庫一樣簡單,具體做法如下:
l 在報表中定義參數(shù)(Bfiledate、Efiledate),
l 設(shè)置集算器數(shù)據(jù)集,并傳遞報表參數(shù),
l 設(shè)計報表統(tǒng)計圖
如下圖所示:
完成報表設(shè)計后,輸入?yún)?shù)進(jìn)行計算,就可以得到希望的報表了。
上一章節(jié)中,通過對歷史數(shù)據(jù)(文件)和實時數(shù)據(jù)(數(shù)據(jù)庫)進(jìn)行混合計算,就能夠輕松實現(xiàn)實時報表(T+0)方案;而為了做到這一點,相應(yīng)的數(shù)據(jù)預(yù)處理,包括怎么導(dǎo)出到文件、設(shè)計怎樣的存儲組織等,也就顯得尤為重要了。
接下來將討論歷史數(shù)據(jù)導(dǎo)出到文件的幾種模式及優(yōu)缺點分析:冷導(dǎo)出、折中辦法、熱導(dǎo)出。
關(guān)于用文件存儲歷史數(shù)據(jù)能夠帶來的諸多好處,可以參考<<基于文件系統(tǒng)實現(xiàn)可追加的數(shù)據(jù)集市>>的相關(guān)章節(jié),這里不再贅述。
所謂冷導(dǎo)出,就是允許有一段 “時間窗口”,能夠從生產(chǎn)庫取出歷史數(shù)據(jù)追加導(dǎo)出到文件中。例如每天的凌晨2-6點為定時執(zhí)行任務(wù)的時間窗口。
冷導(dǎo)出的缺點也很明顯,在追加數(shù)據(jù)導(dǎo)出到文件的這段時間里,這個文件是不可讀的,也就是說相關(guān)的查詢也無法進(jìn)行了,所以,從本質(zhì)上說,冷導(dǎo)出并沒有真正意義上做到T+0實時查詢(生產(chǎn)系統(tǒng)不停機(jī),查詢系統(tǒng)也不停機(jī))。
不過,這里順便解釋一下:如果使用另一個數(shù)據(jù)庫存儲歷史數(shù)據(jù),就不會有這樣的問題。原因在于關(guān)系型數(shù)據(jù)庫支持事務(wù)一致性,數(shù)據(jù)寫入的同時仍然可以很好地支持查詢。當(dāng)然這樣做肯定也會犧牲一部分性能,當(dāng)每天導(dǎo)出的數(shù)據(jù)量較多時對資源占用相當(dāng)巨大(因為數(shù)據(jù)庫回滾段會很大)。
所以一致性和高性能在一定程度上是矛盾的。數(shù)據(jù)庫雖然有一致性,但數(shù)據(jù)庫本身太慢太貴;而集算器(集文件)可以獲得高性能,但沒有事務(wù)一致性,在維護(hù)數(shù)據(jù)的同時不能參與其他計算。
不過,在對業(yè)務(wù)場景要求不是很高的情況下,冷導(dǎo)出也是夠用了,下面我們還是簡單舉例說明一下如何編寫集算器腳本,獲取昨天的歷史數(shù)據(jù)追加到當(dāng)前集文件中,代碼如下:
A | B | |
1 | =file(“D:/PT/MES-pre.btx”) | =connect("demo") |
2 | =B1.cursor@x("SELECT * FROM meta_resource WHERE DATE_FORMAT(fildate,'%Y-%m-%d')=?", after(date(now()),-1)) | >A1.export@ab(A2) |
A1:按路徑打開需要導(dǎo)出的集文件路徑
B1:連接數(shù)據(jù)庫(demo)
A2:根據(jù)sql創(chuàng)建數(shù)據(jù)庫游標(biāo),獲取昨日數(shù)據(jù),參數(shù)為昨天日期; @x選項是指讀完數(shù)據(jù)庫后關(guān)閉
B2:執(zhí)行結(jié)果追加寫入到集文件
針對”冷導(dǎo)出”方案的不足,比較容易想到的折中辦法就是:歷史數(shù)據(jù)不再按照追加的模式寫入到一個集文件中,而是把文件拆開,讓彼此之間的耦合度更低,互不影響。這樣做的話,就需要考慮以下兩條規(guī)則:
1、每天導(dǎo)出一個獨立的集文件,可以用年月日命名,這樣導(dǎo)出過程中,就不會影響對已導(dǎo)出的歷史數(shù)據(jù)的查詢。
2、在查詢腳本中增加時間范圍判斷,規(guī)避掉導(dǎo)出的”時間窗口”;比如定時任務(wù)的時間窗口為每天凌晨2-6點;在查詢腳本中,可以根據(jù)查詢動作的當(dāng)前時間點進(jìn)行邏輯判斷,如果查詢發(fā)生在當(dāng)天6點以后,說明數(shù)據(jù)導(dǎo)出已經(jīng)完成,那么數(shù)據(jù)來源就是集文件(到昨天為止的歷史數(shù)據(jù))+當(dāng)前數(shù)據(jù)庫(到今天當(dāng)前時間點的新數(shù)據(jù)),若查詢發(fā)生在當(dāng)天6點以前的,那就是集文件(到前天為止的歷史數(shù)據(jù))+當(dāng)前數(shù)據(jù)庫(昨天到今天當(dāng)前時間點的新數(shù)據(jù))。
這種辦法的缺點就是在設(shè)計數(shù)據(jù)存儲組織時,文件會分的比較碎,邏輯判斷部分的代碼也會顯得比較冗長,而文件管理也會麻煩一些。但不管怎樣,還是能夠達(dá)到要求,實現(xiàn)真正意義上的實時報表(T+0)方案。下面介紹一下實現(xiàn)步驟。
歷史數(shù)據(jù)按照業(yè)務(wù)模塊進(jìn)行劃分,每天數(shù)據(jù)存一份集文件。目錄結(jié)構(gòu)為:/業(yè)務(wù)模塊/數(shù)據(jù)明細(xì)表/年月日文件名,如下圖所示:
改造“冷導(dǎo)出”方案中數(shù)據(jù)導(dǎo)出腳本,從數(shù)據(jù)庫中獲取昨天的歷史數(shù)據(jù)每天存一份集文件,用年月日命名,代碼如下:
A | B | |
1 | =file(“D:/PT/”+string(after(date(now()),-1),"yyyyMMdd")) | =connect("demo") |
2 | =B1.cursor@x("SELECT * FROM meta_resource WHERE DATE_FORMAT(fildate,'%Y-%m-%d')=?", after(date(now()),-1)) | >A1.export@b(A2) |
A1:按路徑打開需要導(dǎo)出的集文件路徑,每天一個,用年月日命名
前面已經(jīng)解釋過的格子的代碼這里不再贅述。
首先,我們需要寫一個工具腳本,主要功能是能夠根據(jù)傳入的開始日期、結(jié)束日期,過濾出需要查詢跨度范圍的多個集文件路徑,同時判斷路徑下的集文件對象是否存在。腳本命名為:判斷讀取文件的范圍.dfx,編寫代碼如下:
A | |
1 | =if(endDate>=date(now()),if(now()>datetime(concat(date(now())," 06:00:00")),after(endDate,-1),after(endDate,-2)),endDate) |
2 | =periods(startDate,A1,1) |
3 | =A2.(path+string(~,"yyyyMMdd")) |
4 | =A3.select(file(~).exists()) |
5 | return A4 |
腳本接收3個參數(shù),開始日期(startDate),結(jié)束日期(endDate),集文件的存儲路徑(path)
A1:當(dāng)傳入的結(jié)束日期>=當(dāng)前系統(tǒng)日期時,并且當(dāng)前時間是在當(dāng)天6點之后的,返回昨天日期,在當(dāng)天6點之前的,返回前天日期,否則就返回傳入的實際結(jié)束日期
A2:根據(jù)開始日期,計算后的結(jié)束日期,默認(rèn)按天間隔獲取日期范圍
A3:循環(huán)A2,通過集文件的存儲路徑與該日期段內(nèi)的年月日進(jìn)行拼接,利用string()函數(shù)進(jìn)行格式化
A4:判斷路徑下的文件是否真實存在,由A5返回實際存在的文件路徑,最終結(jié)果如下圖:
然后,我們需要對前面章節(jié)中“混合運算場景”數(shù)據(jù)查詢的腳本做一些改造,值得注意的是這里將采用多路游標(biāo)的概念,將多個游標(biāo)合并成一個游標(biāo)使用,改造后的腳本如下:
A | B | C | |
1 | =connect("demo") | =A1.query("SELECT code,name FROM watch") | |
2 | =A1.cursor@x("SELECT code,nums FROM meta_resource WHERE "+if(Efiledate>=date(now()),if(now()>datetime(concat(date(now())," 06:00:00")),"DATE_FORMAT(fildate,'%Y-%m-%d')>='"+string(date(now()))+"'","DATE_FORMAT(fildate,'%Y-%m-%d')>='"+string(after(date(now()),-1))+"'"),"1=0")) | ||
3 | =call("D:/PT/判斷讀取文件的范圍.dfx",Bfiledate,Efiledate,"D:/PT/數(shù)據(jù)表A/") | ||
4 | =A3.(file(~).cursor@b()) | =(A2|A4).mcursor() | |
5 | =B4.groups(code:不良代碼,code:不良名稱;sum(nums):不良數(shù)量,sum(nums):不良累計數(shù)量,sum(0):不良累計比率) | ||
6 | >A5.switch(不良名稱,B1:code) | =A5.sum(不良數(shù)量) | =A5.sort(不良數(shù)量:-1) |
7 | >C6.run(不良累計數(shù)量+=不良累計數(shù)量[-1]) | >C6.run(不良累計比率=round((不良累計數(shù)量/B6)*100,2)) | |
8 | =C6.new(不良代碼,不良名稱.name:不良名稱,不良數(shù)量,不良累計數(shù)量,不良累計比率) | ||
9 | return A8 |
前面已經(jīng)解釋過的格子代碼這里不再贅述。
A2:建立數(shù)據(jù)庫游標(biāo),根據(jù)邏輯判斷動態(tài)拼接sql,當(dāng)查詢的結(jié)束日期>=當(dāng)前系統(tǒng)日期時,并且當(dāng)前查詢時間點是在當(dāng)天6點之后的,只查詢當(dāng)天的實時數(shù)據(jù),當(dāng)前查詢時間點發(fā)生在當(dāng)天6點之前的,查詢返回昨天和當(dāng)天的實時數(shù)據(jù);否則都不滿足的情況下,做一次結(jié)果為空的查詢動作,適應(yīng)只查詢歷史數(shù)據(jù)的業(yè)務(wù)場景。
A3:調(diào)用”判斷讀取文件的范圍.dfx”,傳入腳本參數(shù)開始日期、結(jié)束日期的值,獲得起止日期內(nèi)的所有集文件的集合
A4:循環(huán)A3,分別打開每個集文件對象,根據(jù)文件創(chuàng)建游標(biāo),其中cursor()函數(shù)使用@b選項代表從集文件中讀取。
B4:利用集算器提供的多路游標(biāo)概念,把數(shù)據(jù)結(jié)構(gòu)相同的多個游標(biāo)合并成一個游標(biāo)使用。使用時,多路游標(biāo)采用并行計算來處理各個游標(biāo)的數(shù)據(jù),可以通過設(shè)置cs.mcursor(n) 函數(shù)中的n來決定并行數(shù),當(dāng)n空缺時,將按默認(rèn)自動設(shè)置并行數(shù)
A9:最后返回結(jié)果集給報表工具使用
所謂熱導(dǎo)出,是相對于冷導(dǎo)出而言的。熱導(dǎo)出要保證查詢系統(tǒng)永不停機(jī),在導(dǎo)出數(shù)據(jù)的過程中有查詢請求進(jìn)來,依然能夠工作。熱導(dǎo)出一般適用于實時查詢場景要求較高的情況。
熱導(dǎo)出需要利用文件的備份機(jī)制結(jié)合數(shù)據(jù)庫的一致性來實現(xiàn)熱切換動作。為了便于理解,可參考以下邏輯圖:
首先,在數(shù)據(jù)庫中建備份表,主要目的是為了記錄當(dāng)前正在使用的是哪個備份文件,以及從DB中取的熱數(shù)據(jù)的日期范圍,查詢系統(tǒng)啟動時把這個表清空。
其次,導(dǎo)出歷史數(shù)據(jù)到集文件A,同時備份一個文件B,然后在數(shù)據(jù)庫備份表中記錄該文件A,以及設(shè)定從DB中取的熱數(shù)據(jù)的日期(比如某個時刻之后);這個動作在系統(tǒng)初始化運行時,只做一次。
然后,設(shè)計數(shù)據(jù)查詢的流程:
1、在數(shù)據(jù)庫中建狀態(tài)表,當(dāng)數(shù)據(jù)查詢時,先從備份表中查出可用的備份是哪個文件以及熱數(shù)據(jù)的日期范圍,然后加入一條記錄到狀態(tài)表中,表明該備份文件正有一個查詢,當(dāng)查詢完成后將在狀態(tài)表中把這條記錄刪除,可以用自增列的方式。
再次,設(shè)計數(shù)據(jù)導(dǎo)出到集文件的流程:
1、每天凌晨2點執(zhí)行定時任務(wù),先同步歷史數(shù)據(jù)追加到文件B上,當(dāng)導(dǎo)出完成后,修改數(shù)據(jù)庫備份表的記錄為使用文件B,同時修改從DB中取熱數(shù)據(jù)的范圍,以后新產(chǎn)生的查詢動作都將使用文件B
2、檢查并等待狀態(tài)表中A的使用記錄都已清空(基于A的所有查詢都結(jié)束了),這時才會同步歷史數(shù)據(jù)追加到文件A上,否則每等待1分鐘就循環(huán)檢查一次。
3、當(dāng)步驟2的數(shù)據(jù)追加完成后,再修改數(shù)據(jù)庫備份表為使用文件A,以后的新產(chǎn)生的查詢又會回到了使用文件A,從而達(dá)到熱切換的動作。
4、直到等待狀態(tài)表中B的使用記錄都清空(基于B的查詢也都結(jié)束了)
5、整個過程執(zhí)行完成,可以等待下一輪導(dǎo)出
這里需要特別說明的是,備份表、狀態(tài)表必須用數(shù)據(jù)庫作為媒介,從而利用數(shù)據(jù)庫的一致性;不能用文件記錄備份表、狀態(tài)表的內(nèi)容,因為文件無法保持一致性,當(dāng)多任務(wù)并發(fā)時可能就亂了。
第一步,在數(shù)據(jù)庫中定義”備份表”,包含三個字段(文件名稱/邊界時間/標(biāo)識),同時定義”查詢狀態(tài)表”,包含三個字段(唯一標(biāo)識/文件名稱/當(dāng)前系統(tǒng)時間,其中定義唯一標(biāo)識為自增列),數(shù)據(jù)結(jié)構(gòu)分別如下圖示:
第二步,通過集文件A備份一個集文件B,然后在“備份表”中記錄可查詢的備份文件為A,并設(shè)定從DB中取的熱數(shù)據(jù)邊界時間(定義為每日的零點),此步操作如果用集算器腳本執(zhí)行,樣例代碼如下:
A | B | |
1 | =movefile@c(file("D:/PT/MES-A.btx"),"D:/PT/MES-B.btx") | =connect("demo") |
2 | >B1.execute("INSERT INTO backup (name,crashtime,flag) VALUES (?,?,?)","A",date(now()),"WORKING_STATUS") | >B1.close() |
A1:根據(jù)導(dǎo)出的集文件A,復(fù)制備份同樣的文件B
A2:備份文件B完成后,往數(shù)據(jù)表中寫入當(dāng)前可用的集文件A,當(dāng)前系統(tǒng)時間(零點),給定標(biāo)識列為:WORKING_STATUS
第三步,我們需要對前面章節(jié)中“混合運算場景”數(shù)據(jù)查詢的腳本做一些改造,改造后的腳本如下(此例中也支持僅查歷史數(shù)據(jù)的情況):
A | B | |
1 | =connect("demo") | =A1.query("SELECT code,name FROM watch") |
2 | =A1.query@1("SELECT NAME,crashtime FROM BACKUP WHERE flag='WORKING_STATUS'") | =name=A2(1),crashtime=A2(2) |
3 | =A1.cursor("SELECT code,nums FROM meta_resource WHERE "+if(Efiledate>=date(crashtime),"DATE_FORMAT(fildate,'%Y-%m-%d')>='"+string(date(crashtime))+"'","1=0")) | >A1.execute("INSERT INTO status (name,time) VALUES (?,?)",name,now()),uniques =A1.query@1("SELECT @@identity") |
4 | =file(concat("D:/PT/MES-",name,".btx")).cursor@b().select(filedate>=Bfiledate && filedate<Efiledate) | =[A3,A4].conjx() |
5 | =B4.groups(code:不良代碼,code:不良名稱;sum(nums):不良數(shù)量,sum(nums):不良累計數(shù)量,sum(0):不良累計比率) | |
6 | >A5.switch(不良名稱,B1:code) | =A5.sort(不良數(shù)量:-1) |
7 | >B6.run(不良累計數(shù)量+=不良累計數(shù)量[-1]) | =A5.sum(不良數(shù)量) |
8 | >B6.run(不良累計比率=round((不良累計數(shù)量/B7)*100,2)) | =B6.new(不良代碼,不良名稱.name:不良名稱,不良數(shù)量,不良累計數(shù)量,不良累計比率) |
9 | >A1.execute("DELETE FROM STATUS WHERE uniques=?",uniques) | >A1.close() |
10 | return B8 |
前面已經(jīng)解釋過的格子代碼這里不再贅述。
A2:根據(jù)標(biāo)識WORKING_STATUS作為條件,查詢出來當(dāng)前可用的集文件名稱,以及熱數(shù)據(jù)取值的邊界日期時間
B2:定義變量name, crashtime并賦值,便于后面單元格計算引用。
B3:此單元格做了兩步動作,首先,寫入一條記錄到狀態(tài)表中,表明該當(dāng)前備份文件正有一個查詢,其中uniques為自增列;接著在插入記錄后,通過執(zhí)行【SELECT @@IDENTITY】獲取上一條插入語句中生成的自增長字段的值,賦值給變量uniques,便于A9查詢時引用。數(shù)據(jù)庫中的效果如下圖:
A9:當(dāng)查詢完成后,根據(jù)變量uniques的值作為條件,在狀態(tài)表中把這條記錄刪除,效果如下圖:
改造“冷導(dǎo)出”方案中數(shù)據(jù)導(dǎo)出腳本,每天凌晨2點定時執(zhí)行,代碼如下:
A | B | C | |
1 | =file("D:/PT/MES-B.btx") | =connect("demo") | =file("D:/PT/MES-A.btx") |
2 | =B1.cursor("SELECT * FROM meta_resource WHERE DATE_FORMAT(fildate,'%Y-%m-%d')=?", after(date(now()),-1)) | >A1.export@ab(A2) | >B1.execute("UPDATE BACKUP SET NAME = ?,crashtime=? WHERE flag ='WORKING_STATUS'","B",date(now())) |
3 | for connect("demo").query@1x("SELECT COUNT(*) FROM STATUS WHERE NAME='A'")>0 | >sleep(60*1000) | |
4 | =B1.cursor("SELECT * FROM meta_resource WHERE DATE_FORMAT(fildate,'%Y-%m-%d')=?", after(date(now()),-1)) | >C1.export@ab(A4) | >B1.execute("UPDATE BACKUP SET NAME = ?,crashtime=? WHERE flag = 'WORKING_STATUS'","A",date(now())) |
5 | for connect("demo").query@1x("SELECT COUNT(*) FROM STATUS WHERE NAME='B'")>0 | >sleep(60*1000) | |
6 | >B1.close() |
前面已經(jīng)解釋過的格子代碼這里不再贅述。
C2:當(dāng)歷史數(shù)據(jù)同步追加到文件B上,修改數(shù)據(jù)庫”備份表”的記錄為使用文件B,同時修改從DB中取熱數(shù)據(jù)的邊界日期范圍,執(zhí)行結(jié)果如下圖:
A3-B3:循環(huán)查詢”狀態(tài)表”中A的使用記錄是否已清空,若發(fā)現(xiàn)還有基于A的查詢沒有結(jié)束,那就等待1分鐘,然后接著循環(huán),直到基于A的查詢?nèi)拷Y(jié)束;
其中A3的數(shù)據(jù)庫連接表達(dá)式需要特別說明一下:通常情況下,在B1單元格中已經(jīng)定義了數(shù)據(jù)庫連接,在A3中,可直接引用,寫成:
for B1.query@1("SELECT COUNT(*) FROM STATUS WHERE NAME='A'")>0
不過,有些數(shù)據(jù)庫在默認(rèn)情況下做成了一次連接只處理一個事務(wù),這樣會導(dǎo)致A3在循環(huán)的時候結(jié)果不會變化,總是按照第一次查詢出來的結(jié)果為主,比如第一次查詢返回是true,當(dāng)數(shù)據(jù)庫發(fā)生變化了,它還是返回true,為了保險期間,可以寫成如下格式:
for connect("demo").query@1x("SELECT COUNT(*) FROM STATUS WHERE NAME='A'")>0
這個屬于數(shù)據(jù)庫配置的范疇,可以通過數(shù)據(jù)庫的連接參數(shù)來控制,這里不再詳解。
C4:當(dāng)文件A的數(shù)據(jù)追加完成后,再修改數(shù)據(jù)庫”備份表”的記錄為使用文件A,以后新產(chǎn)生的查詢就會再使用文件A,執(zhí)行結(jié)果如下圖:
A5-B5:循環(huán)查詢”狀態(tài)表”中B的使用記錄是否已清空,若發(fā)現(xiàn)還有基于B的查詢沒有結(jié)束,那就等待1分鐘,然后接著循環(huán),直到基于B的查詢?nèi)拷Y(jié)束, 此輪整個導(dǎo)出過程全部完成,然后等待下一輪導(dǎo)出
實時報表(T+0)的場景下,數(shù)據(jù)的熱導(dǎo)出是個有些復(fù)雜的話題,不過,利用集算器(集文件)的備份機(jī)制結(jié)合數(shù)據(jù)庫的一致性就可以輕松應(yīng)對這類難題了,其中主要用到了以下兩個優(yōu)勢:
1、跨庫混合計算
集算器作為獨立的計算引擎,可以并行指揮各個數(shù)據(jù)庫分別計算,收集結(jié)果后再進(jìn)行一輪匯總運算,然后向前端提交或者落地,從而可以很簡單的實現(xiàn)T+0全量查詢報表。
同時,在集算器跨庫混合計算模型下,也不要求數(shù)據(jù)庫是否同構(gòu),歷史數(shù)據(jù)可以選擇存儲在成本更低的開源數(shù)據(jù)庫中,例如Oracle和MySQL的混搭集群。
2、高性價比、高性能的集文件
無需構(gòu)建數(shù)倉,將歷史數(shù)據(jù)外置存放到文件系統(tǒng)中,不僅便于管理,而且可以獲得更高效的IO性能和計算能力,從而很好的解決了關(guān)系型數(shù)據(jù)庫中由于數(shù)據(jù)量大而導(dǎo)致的性能瓶頸和存儲成本。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。