溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

實時報表 T+0 的實現(xiàn)方案

發(fā)布時間:2020-07-17 22:18:46 來源:網(wǎng)絡(luò) 閱讀:1472 作者:raqsoft 欄目:大數(shù)據(jù)

基于數(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)也變得更加清新與合理了:

實時報表 T+0 的實現(xiàn)方案

上圖新處理方式體系結(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方案的。報表最終的展示效果如下圖:

實時報表 T+0 的實現(xiàn)方案

這張報表清楚地顯示了電子設(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ù)模板”功能,具體做法參見教程,這里不再贅述。

3.1編寫數(shù)據(jù)查詢腳本

我們假定已經(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ù)量、不良累計比率,方便后面的賦值計算。

實時報表 T+0 的實現(xiàn)方案

A5:通過switch()函數(shù)在A4結(jié)果的”不良名稱”字段上建立指向B1表中code字段的指針引用記錄,實現(xiàn)關(guān)聯(lián),如下圖:

實時報表 T+0 的實現(xiàn)方案

B5:按照不良數(shù)量降序排列。如下圖:

實時報表 T+0 的實現(xiàn)方案

A6:計算不良累計數(shù)量;可以看到,集算器用“不良累計數(shù)量[-1]”來表示上一行的不良數(shù)量,可以輕松進(jìn)行相對位置的計算。

實時報表 T+0 的實現(xiàn)方案

B6:對不良數(shù)量進(jìn)行總計

A7:計算出不良累計比率,算法為“(不良數(shù)量累計匯總/總不良數(shù)量匯總)*100”,同時保留兩位小數(shù),計算結(jié)果如下圖:

實時報表 T+0 的實現(xiàn)方案

B7-A8:取出需要的字段,將關(guān)聯(lián)了不良名稱后的結(jié)果集返回給報表工具,如下圖:

實時報表 T+0 的實現(xiàn)方案

3.2作為報表數(shù)據(jù)源

在利用集算器完成了數(shù)據(jù)查詢工作后,為了在報表中使用查詢結(jié)果,可以在報表中直接將集算器設(shè)置為數(shù)據(jù)源,用法和使用數(shù)據(jù)庫一樣簡單,具體做法如下:

l   在報表中定義參數(shù)(Bfiledate、Efiledate),

l   設(shè)置集算器數(shù)據(jù)集,并傳遞報表參數(shù),

l   設(shè)計報表統(tǒng)計圖

如下圖所示:

實時報表 T+0 的實現(xiàn)方案

完成報表設(shè)計后,輸入?yún)?shù)進(jìn)行計算,就可以得到希望的報表了。

四 數(shù)據(jù)預(yù)處理

上一章節(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)出。

4.1冷導(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é)果追加寫入到集文件

4.2折中辦法

針對”冷導(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)步驟。

4.2.1設(shè)計數(shù)據(jù)存儲組織

歷史數(shù)據(jù)按照業(yè)務(wù)模塊進(jìn)行劃分,每天數(shù)據(jù)存一份集文件。目錄結(jié)構(gòu)為:/業(yè)務(wù)模塊/數(shù)據(jù)明細(xì)表/年月日文件名,如下圖所示:

實時報表 T+0 的實現(xiàn)方案

4.2.2同步昨天數(shù)據(jù)到文件

改造“冷導(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)解釋過的格子的代碼這里不再贅述。

4.2.3數(shù)據(jù)查詢

首先,我們需要寫一個工具腳本,主要功能是能夠根據(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é)果如下圖:

實時報表 T+0 的實現(xiàn)方案

然后,我們需要對前面章節(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é)果集給報表工具使用

4.3熱導(dǎo)出

所謂熱導(dǎo)出,是相對于冷導(dǎo)出而言的。熱導(dǎo)出要保證查詢系統(tǒng)永不停機(jī),在導(dǎo)出數(shù)據(jù)的過程中有查詢請求進(jìn)來,依然能夠工作。熱導(dǎo)出一般適用于實時查詢場景要求較高的情況。

4.3.1實現(xiàn)思路

熱導(dǎo)出需要利用文件的備份機(jī)制結(jié)合數(shù)據(jù)庫的一致性來實現(xiàn)熱切換動作。為了便于理解,可參考以下邏輯圖:

實時報表 T+0 的實現(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ā)時可能就亂了。

4.3.2數(shù)據(jù)查詢

第一步,在數(shù)據(jù)庫中定義”備份表”,包含三個字段(文件名稱/邊界時間/標(biāo)識),同時定義”查詢狀態(tài)表”,包含三個字段(唯一標(biāo)識/文件名稱/當(dāng)前系統(tǒng)時間,其中定義唯一標(biāo)識為自增列),數(shù)據(jù)結(jié)構(gòu)分別如下圖示:

實時報表 T+0 的實現(xiàn)方案

第二步,通過集文件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ù)庫中的效果如下圖:

實時報表 T+0 的實現(xiàn)方案

A9:當(dāng)查詢完成后,根據(jù)變量uniques的值作為條件,在狀態(tài)表中把這條記錄刪除,效果如下圖:

實時報表 T+0 的實現(xiàn)方案

4.3.3同步數(shù)據(jù)與熱切換

改造“冷導(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é)果如下圖:

實時報表 T+0 的實現(xiàn)方案

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é)果如下圖:

實時報表 T+0 的實現(xiàn)方案

A5-B5:循環(huán)查詢”狀態(tài)表”中B的使用記錄是否已清空,若發(fā)現(xiàn)還有基于B的查詢沒有結(jié)束,那就等待1分鐘,然后接著循環(huán),直到基于B的查詢?nèi)拷Y(jié)束, 此輪整個導(dǎo)出過程全部完成,然后等待下一輪導(dǎo)出

五 總結(jié)

實時報表(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)致的性能瓶頸和存儲成本。


向AI問一下細(xì)節(jié)

免責(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)容。

AI