10行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化-創(chuàng)新互聯(lián)

大話數(shù)據(jù)計(jì)算性能優(yōu)化

大數(shù)據(jù)分析的性能優(yōu)化,說道底,就優(yōu)化一個事情:針對確定的一個計(jì)算任務(wù)(數(shù)據(jù)確定,結(jié)果確定),以最經(jīng)濟(jì)的方案得到結(jié)果。

創(chuàng)新互聯(lián)建站長期為上千多家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為大竹企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì),大竹網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

這個最經(jīng)濟(jì)的方案主要考量三個成本:時(shí)間成本、硬件成本、軟件成本。

  • 時(shí)間成本:根據(jù)計(jì)算任務(wù)的特點(diǎn),能容忍的最長時(shí)間各不相同。那些 T+0 的計(jì)算任務(wù),實(shí)時(shí)性要求就比較高,T+1 再算出結(jié)果就失去了意義。

  • 硬件成本:可以使用的硬件資源,對一個公司來說一般不是經(jīng)常變化的,機(jī)器配置、可集群數(shù)量就那么多。即便使用云計(jì)算產(chǎn)品,也只是多了擴(kuò)容的靈活性,成本是少不掉的。

  • 軟件成本:編寫出這個計(jì)算算法的人工費(fèi) + 軟件環(huán)境的成本。這個成本也與前兩項(xiàng)相關(guān),程序控制力度粗獷一些,實(shí)現(xiàn)邏輯簡單一些,程序就容易編寫,那軟件成本就會低一些,帶來的副作用是運(yùn)行時(shí)間超長或者需要昂貴的硬件。

這三個因素里面,一般對于計(jì)算任務(wù)來說,自然是越快越好,當(dāng)然只要不慢過能容忍的時(shí)長,也就還算是有意義的計(jì)算;而硬件因素的彈性就比較小,有多少資源是相對固定的;所以,剩下的可以大做文章的就是軟件成本了。軟件成本里,程序員的工資是很重要的一項(xiàng),而有沒有順手的軟件環(huán)境讓程序員能高效的把計(jì)算描述出來,就成了關(guān)鍵。最典型的例子就是理論上用匯編程序能寫出所有的程序,但它明顯不如  SQL 或 JAVA 做個常規(guī)計(jì)算來的容易。

說到 SQL 和 JAVA,成規(guī)模的計(jì)算中心的一些維護(hù)者估計(jì)也會皺眉,使用它們的時(shí)間越長,越能體會需求變動或優(yōu)化算法過程中的痛苦,明明算法過程自己想的很清楚了,但編寫成可運(yùn)行的程序就困難重重。這些困難主要來自兩個方面:

  • 首先,一些基礎(chǔ)的數(shù)據(jù)操作方法是自己逐漸積累的,沒有經(jīng)過整體的優(yōu)化設(shè)計(jì),這些個人工具對個人的開發(fā)效率有不錯的提升,但沒法通用,也不全面,這個困難主要表現(xiàn)在用 JAVA 等高級語言實(shí)現(xiàn)的一些 UDF 上。

  • 第二,主要是思維方式上的,在生產(chǎn)場景下用習(xí)慣了 SQL 查詢,在計(jì)算場景下遇到的性能問題自然而然就想通過優(yōu)化  SQL  語句的方式把問題緩解掉。但實(shí)際上這可能是個溫水煮青蛙的過程。越深入搞,把簡單的過程問題越可能搞成龐大不可拆分的邏輯塊,到最后只有原創(chuàng)作者或高手才敢碰它。我這個老程序員,十多年前剛?cè)胄械臅r(shí)候,八卦中耳聞過  ORACLE 的系統(tǒng)管理員,尤其是有性能優(yōu)化能力的,比普通程序員貴多了,可見這個難題在數(shù)據(jù)規(guī)模相對較小的十年前已經(jīng)凸顯了。

(注:生產(chǎn)場景計(jì)算場景在初始階段的軟件系統(tǒng)里一般很難截然分開,數(shù)據(jù)都是從生產(chǎn)場景積累起來的,等積累多了,慢慢會增加計(jì)算需求,逐漸獨(dú)立出計(jì)算中心和數(shù)據(jù)倉庫。這個量變引起質(zhì)變的過程,如果不在思維上轉(zhuǎn)變,不引入新辦法,那就將成為被煮的青蛙。)

為了節(jié)省讀者的時(shí)間,我們先把性能優(yōu)化的常用手段總結(jié)一下,方便有需求的用戶逐條對比進(jìn)行實(shí)際操作。

1、 只加載計(jì)算相關(guān)數(shù)據(jù)。

  • 列存方式存儲數(shù)據(jù);

  • 常用的字段和不常用的分開存儲;

  • 用獨(dú)立的維表存儲維的屬性,減少事實(shí)表的信息冗余;

  • 按照某些常用作查詢條件的字段分開存儲,如按年份、性別、地區(qū)等獨(dú)立存儲;

2、 精簡計(jì)算涉及到的數(shù)據(jù)

  • 用來分析時(shí),一些冗長的編號,可以序號化處理,用 1、2、……替代 TJ001235-078、 TJ001235-079、……,這樣即能加快加載數(shù)據(jù)的速度,又能加快計(jì)算速度。

  • 日期時(shí)間,如果用字符串類型按照我們熟悉的格式 (2011-03-08) 存儲,那加載和計(jì)算都會慢。前面這個日期可以存儲成 110308 這樣的數(shù)值類型,也可以存儲成相對于一個開始時(shí)間的毫秒數(shù)(如相對于最早的數(shù)據(jù) 2010-01-01 的毫秒數(shù))。

3、 算法的優(yōu)化

  • 計(jì)算量小的條件寫在前面,如 boolean 類型的判斷,要早于字符串查找,這樣用較少的計(jì)算就能排除掉不符合要求的數(shù)據(jù);

  • 減少對大事實(shí)表的遍歷次數(shù)。具體方法有:在一次遍歷過程中,同時(shí)給多個獨(dú)立的運(yùn)算操作提供數(shù)據(jù)(后面會提到的集算器里的管道概念),而不是每個運(yùn)算操作遍歷一次數(shù)據(jù);做 JOIN 時(shí),在內(nèi)存里的維表里檢索事實(shí)表數(shù)據(jù),而不是用每條維表數(shù)據(jù)去遍歷一次事實(shí)表。

  • 查找時(shí)借用 HASH 索引、二分法、序號直接對位等方式加快速度。

4、 并行計(jì)算

  • 加載數(shù)據(jù)和計(jì)算兩個步驟都可以并行。考量計(jì)算特點(diǎn),根據(jù)加載數(shù)據(jù)和運(yùn)算哪個量更大來判斷瓶頸是計(jì)算機(jī)的磁盤還是 CPU,磁盤陣列適合并行加載數(shù)據(jù),多核 CPU 適合并行運(yùn)算。

  • 多機(jī)集群的并行任務(wù),要考慮主程序和子程序的通訊問題,盡量把復(fù)雜計(jì)算獨(dú)立到節(jié)點(diǎn)機(jī)上完成,網(wǎng)絡(luò)傳輸較慢,要減少節(jié)點(diǎn)機(jī)之間的數(shù)據(jù)交換。

實(shí)操效果

兵馬未動糧草先行,有了上面這些指導(dǎo)思想,我們下面就切入正題實(shí)現(xiàn)漏斗計(jì)算的優(yōu)化,看一下實(shí)際的優(yōu)化效果。

1、未做任何優(yōu)化,直接開工

數(shù)據(jù):

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

程序:(附件中的 1-First.dfx,也附帶了測試數(shù)據(jù)文件,可在集算器里直接執(zhí)行)

      漏斗轉(zhuǎn)換計(jì)算核心代碼的邏輯細(xì)節(jié)在上一篇中詳細(xì)介紹過,這里就不再贅述。

結(jié)果:(注:之后的測試都以 118 萬條數(shù)據(jù)為基礎(chǔ),成倍增加)

      118 萬條記錄 /70MB/ 用戶數(shù)量 8000/31 秒;

      590 萬條記錄 /350MB/ 用戶數(shù)量 4 萬 /787 秒。

分析:

      數(shù)據(jù)量增加到 5 倍,但耗時(shí)增加到了 26 倍,性能下降得厲害,而且不是線性的。原因是被分析的用戶列表擴(kuò)大了 5 倍,同時(shí)被分析的記錄數(shù)也擴(kuò)大 5 倍,那檢索用戶次數(shù)理論上就擴(kuò)大了 5*5 倍。接下來采用以下優(yōu)化方式↓

2、按用戶 ID 順序插入用戶列表,用二分法查找用戶。

程序:(2-BinarySearch.dfx)

      B12 給 find 增加 @b 選項(xiàng),指明用二分法查找;D13 中卻去掉 insert 的第一個位置參數(shù) 0 后,新用戶就不直接追加到最后了,而是按主鍵順序插入。

A

B

C

D

11

……

12

for A11

>user=A10.find@b(A12.用戶 ID)

13

if user==null

if A12.事件 ID==events(1)

>A10.insert(,A12.用戶 ID: 用戶 ID,1:maxLen,[[A12. 時(shí)間,1]]:seqs)

14

……

結(jié)果:

      118 萬條記錄 /70MB/ 用戶數(shù)量 8000/10 秒;

      590 萬條記錄 /350MB/ 用戶數(shù)量 4 萬 /47 秒。

分析:

       優(yōu)化后,1 倍的數(shù)據(jù)量耗時(shí)縮減到 1/3;5 倍的數(shù)據(jù)量提速比較明顯,縮減到 1/16。進(jìn)一步觀察,5 倍數(shù)據(jù)量是  350MB,從硬盤載入數(shù)據(jù)的速度慢點(diǎn)算也會有 100M/ 秒,假如 CPU 夠快的話,極限速度應(yīng)該能到 4 秒左右,而現(xiàn)在的 47 秒證明  CPU 耗時(shí)還比較嚴(yán)重,根據(jù)經(jīng)驗(yàn)可以繼續(xù)優(yōu)化↓

3、批量讀入游標(biāo)數(shù)據(jù)

程序:(3-BatchReadFromCursor.dfx)

      12~17 行整體剪切后,向右移一個格子之后,在 A12 增加一個批量加載游標(biāo)數(shù)據(jù)的循環(huán),表示 A11 中的游標(biāo)每次取 10000 條,B12 再對取出來的這 10000 條數(shù)據(jù)循環(huán)處理。

A

B

C

11

……

12

for A11,10000

for A12

……

13

……

結(jié)果:

      118 萬條記錄 /70MB/ 用戶數(shù)量 8000/4 秒;

      590 萬條記錄 /350MB/ 用戶數(shù)量 4 萬 /10 秒;

      5900 萬條記錄 /3.5GB/ 用戶數(shù)量 40 萬 /132 秒;

      11800 萬條記錄 /7GB/ 用戶數(shù)量 80 萬 /327 秒。

分析:

       優(yōu)化后,1 倍數(shù)據(jù)量耗時(shí)縮減到 2/5;5 倍的數(shù)據(jù)量縮減到 1/5;新測試的 50 倍、100  倍性能也大體隨數(shù)據(jù)量保持了線性。注意到原始數(shù)據(jù)有一些字段用不到,用到的字段也可以通過序號化等手段再簡化,簡化后的文件會小幾倍,從而達(dá)到從硬盤減少讀取時(shí)間的目的,具體優(yōu)化方式如下↓

4、精簡數(shù)據(jù)

思路:

       先觀察一下原始數(shù)據(jù):用戶 ID 用從 1  開始的序號替代,除了減少少許存儲空間外,還可以在后續(xù)計(jì)算時(shí)通過序號快速定位到用戶,減少查找時(shí)間。時(shí)間和年月日字段信息重復(fù),去掉年月日,長整型的時(shí)間字段也可以進(jìn)一步精簡成相對  2017-01-01 這個開始時(shí)間的毫秒數(shù);事前我們知道只有 10 種事件,那事件 ID  和事件名稱可以單獨(dú)提取出個維表記錄,這個事實(shí)表里只保存序號化的事件 ID(1、2、3…10)就夠了;事件屬性是 JSON  格式,種類不多,那對于某一種事件,可以用序列存儲事件屬性的值,在序列中的位置表示某種屬性,這樣即縮減存儲空間,又能提升查找屬性的效率。

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

      除了上面這些字段值的精簡,我們存儲數(shù)據(jù)的格式棄用文本方式,改變成集算器二進(jìn)制格式,存儲空間更小,加載速度更快,精簡后的事實(shí)表如下:

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

實(shí)現(xiàn):

      精簡事實(shí)表數(shù)據(jù)之前,要先通過事實(shí)表生成用戶表、事件表兩個維表的(genDims.dfx,運(yùn)行后生成 user.bin 和 event.bin):

A

1

>beginTime=now()

2

>fPath="e:/ldsj/demo/"

3

=file(fPath+"src-11800.txt").cursor@t()

4

=channel(A3)

5

=A4.groups(#1:用戶 id)

6

=A3.groups(#3:事件 ID,#4: 事件名稱; iterate(~~&#5.import@j().fname()): 屬性名稱)

7

=file(fPath+"event.bin").export@b(A6)

8

=file(fPath+"user.bin").export@b(A4.result())

9

=interval@s(beginTime,now())

       提取維表的這段程序,仍然有優(yōu)化的手段體現(xiàn)。提取兩個維表,常規(guī)思維是每遍歷一遍數(shù)據(jù),生成一個維表;從硬盤讀入大量數(shù)據(jù)進(jìn)行遍歷,讀入慢,但讀入后的計(jì)算量卻非常小。針對這種情況,那有什么手段可以在讀入數(shù)據(jù)時(shí),同時(shí)用于多種獨(dú)立的計(jì)算呢,答案就是“管道”,多定義了幾個管道,就多定義了幾種運(yùn)算。A4  針對 A3 游標(biāo)定義管道,A5 定義 A4 管道的分組計(jì)算,A6 定義另外一個分組計(jì)算,A7 導(dǎo)出 A6 的結(jié)果,A8 導(dǎo)出 A4  管道的結(jié)果。最終得到的兩個維表如下:

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

      基于上面兩個維表對事實(shí)表進(jìn)行精簡(toSeq.dfx),6.8G 的文本文件精簡后,得到 1.9G 的二進(jìn)制文件,縮小了 3.5 倍。

A

B

C

D

1

>beginTime=now()

2

>fPath="e:/ldsj/demo/"

3

=file(fPath+"src-11800.txt").cursor@t()

4

=file(fPath+"event.bin").import@b()

=A4.(事件 ID)

=A4.(屬性名稱 )

5

=file(fPath+"user.bin").import@b()

=A5.(用戶 ID)

6

7

func

8

=A7.import@j()

9

=[]

10

for B7

11

>B9.insert(0,eval("B8."+B10))

12

return B9

13

14

for A3,10000

15

=A14.new(C5.pos@b(用戶 ID): 用戶 ID,C4.pos@b( 事件 ID): 事件 ID, 時(shí)間: 時(shí)間,func(A7, 事件屬性,D4(C4.pos@b( 事件 ID))): 事件屬性 )

16

=file(fPath+"src-11800.bin").export@ab(B15)

17

=interval@s(beginTime,now())

      這段代碼出現(xiàn)了一個新的知識點(diǎn),第 7~12 行定義了一個函數(shù)來處理 json 格式的事件屬性,B15 里精簡每一行數(shù)據(jù)時(shí),調(diào)用了這個函數(shù)。B16 把每次精簡好的一萬條記錄追寫入同一個二進(jìn)制文件。

程序:(4-Reduced.dfx)

      在上一次程序的基礎(chǔ)上改造了這么幾個格子:

      A3/A4 中的時(shí)間相對于 2017-01-01;

      A6 事件序列改用序號;

      A7 中屬性過濾,用精確匹配值的方式替換以前低效的模糊匹配字符串方式;       A10 初始化用戶序列,長度為用戶數(shù),該序列中的位置代表用戶的序號;

      C12 用序號方式查找用戶;

      E13 用序號方式存儲新用戶:

A

B

C

D

E

2

……

3

>begin=interval@ms(date(2017,1,1),date(2017,1,1))

4

>end=interval@ms(date(2017,1,1),date(2017,3,1))

5

>dateWindow=10*24*60*60*1000

6

>events=[3,4,6,7]

7

>filter="if(事件 ID!=4||(事件屬性.len()>0&& 事件屬性 (1)==\"Apple\");true)"

8

9

/開始執(zhí)行漏斗轉(zhuǎn)換計(jì)算程序

10

=to(802060).(null)

11

=file(dataFile).cursor@b().select(時(shí)間 >=begin&& 時(shí)間 <end &&  events.pos(事件 ID)>0 && ${filter})

12

for A11,10000

for A12

>user=A10(B12.用戶 ID)

13

if user==null

if B12.事件 ID==events(1)

>A10(B12.用戶 ID)=[B12. 用戶 ID,1,[[B12. 時(shí)間,1]]]

14

……

結(jié)果:

      11800 萬條記錄 /1.93GB/ 用戶數(shù)量 80 萬 /225 秒。

分析:

      優(yōu)化后,100 倍數(shù)據(jù)量耗時(shí)縮減到上一步的 2/3。除了精簡涉及的查詢字段,我們再看看另一種能有效縮減查詢數(shù)據(jù)量的方法↓

5、把數(shù)據(jù)預(yù)先拆分存儲,計(jì)算的時(shí)候只加載涉及到的數(shù)據(jù)

思路:

      如何拆分?jǐn)?shù)據(jù)和查詢特點(diǎn)有關(guān),這個例子中經(jīng)常查詢不定時(shí)間段,那按照日期拆分比較合適,按照事件 ID 拆分就沒有意義了。

拆分?jǐn)?shù)據(jù)的程序(splitData.dfx):

      A4 每次取出 10 萬條數(shù)據(jù);B4 循環(huán) 60 天;C6 按照日期查詢到數(shù)據(jù)后,通過 C9 追加到各自日期的文件里。

A

B

C

D

1

=dataFile=file("e:/ldsj/demo/src-11800.bin").cursor@b()

2

>destFolder="e:/ldsj/demo/dates/"

3

>oneDay=24*60*60*1000

4

for A1,100000

for 60

>begin=long(B4-1)*oneDay

5

>end=long((B4))*oneDay

6

=A4.select(時(shí)間 >=begin &&  時(shí)間 <end)

7

if (C6 == null)

next

8

>filename= string(date(long(date(2017,1,1))+begin), "yyyyMMdd")+".bin"

9

=file(destFolder+fileName).export@ab(C6)

      執(zhí)行后生成 59 天的數(shù)據(jù)文件:

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

程序:(5-SplitData.dfx)

      A2 中把以前被分析的文件定義換成目錄;

      A3/A4 的起止日期條件有所變動,以前是查詢?nèi)掌谧侄?,現(xiàn)在變成查找日期文件;

      A11 把目錄下的日期文件排序,選出要分析的多個日期文件,然后組合成一個游標(biāo)之后再進(jìn)行事件過濾就可以了。

A

1

……

2

>fPath="e:/ldsj/demo/dates/"

3

>begin="20170201.bin"

4

>end="20170205.bin"

5

……

11

=directory(fPath+"2017*").sort().select(~>=begin&&~<=end).(file(fPath+~:"UTF-8")).(~.cursor@b()).conjx().select(events.pos(事件  ID)>0 && ${filter})

12

……

結(jié)果:

      目標(biāo)數(shù)據(jù)選擇 2017-02-01 至 2017-02-05 這 5 天,全量掃描數(shù)據(jù) 168 秒;只掃描 5 個文件得到相同結(jié)果 7 秒,效果顯著。到目前為止,讀取數(shù)據(jù)和計(jì)算都是單線程的,下面我們再試試并行計(jì)算↓

6、并行計(jì)算

單線程加載數(shù)據(jù),多線程計(jì)算

程序:(6-mulit-calc.dfx)

      增加 B 列,B2 中啟動 4 個線程處理 A12 里加載的 100000 條數(shù)據(jù),C12 中依據(jù)用戶 ID%4 的余數(shù)分成 4 組,分別給 4 個線程進(jìn)行運(yùn)算。

A

B

C

11

……

12

for A11,100000

fork to(4)

for A12.select(用戶 ID%4==B12-1)

13

……

結(jié)果:

      11800 萬條 /1.93GB/ 用戶數(shù) 80 萬 /4 線程 / 一次性讀入 10 萬條數(shù)據(jù) /262 秒;

      11800 萬條 /1.93GB/ 用戶數(shù) 80 萬 /4 線程 / 一次性讀入 40 萬條數(shù)據(jù) /161 秒;

      11800 萬條 /1.93GB/ 用戶數(shù) 80 萬 /4 線程 / 一次性讀入 80 萬條數(shù)據(jù) /233 秒;

      11800 萬條 /1.93GB/ 用戶數(shù) 80 萬 /4 線程 / 一次性讀入 400 萬條數(shù)據(jù) /256 秒。

分析:

      筆者測試機(jī)器是單個機(jī)械硬盤,加載數(shù)據(jù)速度是瓶頸,所以對提速不太明顯。但調(diào)整單次加載的數(shù)據(jù)量,還是會有明顯的性能差異。每次處理 40 萬條數(shù)據(jù)時(shí)性能最優(yōu)。

多線程加載數(shù)據(jù)

預(yù)處理:(splitDataByUserId.dfx)

      雖然 4 個線程可以同時(shí)讀全量數(shù)據(jù)的同一個文件,但每個線程讀出 3/4 的無用數(shù)據(jù)必然拖慢速度,所以預(yù)先按照用戶 ID%4 拆分一下文件能更快些。C3 查詢出 ID%4 的數(shù)據(jù),C6 把查詢的數(shù)據(jù)存入相應(yīng)的拆分文件。

A

B

C

D

1

=file("e:/ldsj/demo/src-11800.bin").cursor@b()

2

e:/ldsj/demo/users/

3

for A1,100000

for to(4)

=A3.select(用戶 ID%4==B3-1)

4

if (C3 == null)

next

5

="src-11800-"+string(B3)+".bin"

6

=file(A2+C5).export@ab(C3)

程序:(6-mulit-read.dfx)

      把多線程代碼前移到 A11,每個線程內(nèi)讀取各自的文件進(jìn)行計(jì)算 (B11)。

A

B

C

9

……

10

=to(802060).(null)

11

fork to(4)

=file(fPath+"src-11800-"+string(A11)+".bin").cursor@b().select(時(shí)間  >=begin&& 時(shí)間 <end  && events.pos(事件 ID)>0  &&  ${filter})

12

for B11,10000

……

13

……

結(jié)果:

      11800 萬條記錄 /1.93GB/ 用戶數(shù)量 80 萬 /4 線程 /113 秒。

分析:

      同樣受限于加載數(shù)據(jù)速度,提速也有限。如果用多臺機(jī)器集群,每臺機(jī)器處理 1/4 的數(shù)據(jù),因?yàn)槭嵌鄠€硬盤并行,速度肯定會有大幅提升,下面我們就看一下如何實(shí)現(xiàn)多機(jī)并行↓

多機(jī)集群并行計(jì)算

      集算器如何部署集群計(jì)算,如何寫集群的主、子程序的知識點(diǎn)不是本文重點(diǎn)關(guān)注的,可以移步相關(guān)的文檔詳細(xì)了解:http://doc.raqsoft.com.cn/esproc/tutorial/jqjs.html。

主程序:(6-multi-pc-main.dfx)

      A3 中用 callx 調(diào)用子程序 6-multi-pc-sub.dfx,參數(shù)序列 [1,2,3…] 傳入每個子程序控制處理哪一部分?jǐn)?shù)據(jù);返回的結(jié)果再通過 B6 匯總到一起,結(jié)果存放在 A4 格子里。

A

B

1

>beginTime=now()

2

[127.0.0.1:8281,127.0.0.1:8282]

3

=callx("e:/ldsj/demo/6-multi-pc-sub.dfx",to(2),"e:/ldsj/demo/users/";A2)

4

5

for A3

6

>A4=if(A4==null,A5,A4++A5)

7

=interval@s(beginTime,now())

      A3 得到結(jié)果序列:

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

      A4 匯總出最終結(jié)果:

10 行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化

節(jié)點(diǎn)機(jī)子程序:(6-multi-pc-sub.dfx)

      相比較上一步單機(jī)多線程加載數(shù)據(jù)的程序,去掉 A11 的多線程 fork to(4);節(jié)點(diǎn)機(jī)計(jì)算哪個拆分文件是通過 taskSeq 參數(shù)由主程序傳過來的(B11);A22 把 A20 里的結(jié)果返回給主程序。

A

B

C

9

……

10

=to(802060).(null)

11

=file(fPath+"src-11800-"+string(taskSeq)+".bin").cursor@b().select(時(shí)間  >=begin&& 時(shí)間 <end &&  events.pos(事件 ID)>0  && ${filter})

12

for B11,10000

……

13

……

22

return A20

結(jié)果:

      11800 萬條記錄 /1.93GB/ 用戶數(shù)量 80 萬 / 單節(jié)點(diǎn)機(jī)處理四分之一數(shù)據(jù) /38 秒。主程序匯總的時(shí)間很短忽略不計(jì),也就是 4 個 PC 的四塊硬盤并行加載數(shù)據(jù)時(shí),能把速度提升到 38 秒。

      程序和測試數(shù)據(jù)在百度網(wǎng)盤下載 。安裝好集算器,修改下程序里的文件路徑,就可以運(yùn)行看效果了。

結(jié)束語 - 前瞻

       看到上面這么多的優(yōu)化細(xì)節(jié),估計(jì)有人質(zhì)疑,這么費(fèi)力的把這事做到極致,是不是吹毛求疵了?數(shù)據(jù)庫應(yīng)該是內(nèi)置了一些自動的優(yōu)化算法,目前已有共識的是尤其  ORACLE  在這方面已經(jīng)做的很細(xì)致,這些細(xì)節(jié)根本不需要用戶操心。確實(shí),自動性能優(yōu)化的重要意義是肯定的,但近幾年隨著數(shù)據(jù)環(huán)境的復(fù)雜化,數(shù)據(jù)量的劇增,更精細(xì)的控制數(shù)據(jù)的能力也就有了越來越多的應(yīng)用場景,雖然會增加學(xué)習(xí)成本,但也會帶來更高的數(shù)據(jù)收益。而且這個學(xué)習(xí)成本除了解決性能問題外,還能更好地解決根本上的描述復(fù)雜計(jì)算、整理數(shù)據(jù)方面的業(yè)務(wù)需求,更何況這類問題是無法自動化的,因?yàn)槭恰皼Q策要做什么”變復(fù)雜了,因此只能提供更方便的編程語言提高描述效率,正視問題。計(jì)算機(jī)再智能,也不能替代人類做決策。自動和手動兩種方式不是對立,而是互補(bǔ)的關(guān)系!

上面這些優(yōu)化的思路是我們程序員能預(yù)先想到的,同時(shí)也大概能根據(jù)計(jì)算任務(wù)特點(diǎn)選擇效果顯著的優(yōu)化方式。但我要說的是計(jì)算機(jī)系統(tǒng)太復(fù)雜了:特點(diǎn)迥異的計(jì)算需求、不穩(wěn)定的硬盤讀寫速度、不穩(wěn)定的網(wǎng)絡(luò)速度、無法估量的  CPU 具體計(jì)算量!所以實(shí)際業(yè)務(wù)中我們還需要依靠經(jīng)驗(yàn)根據(jù)實(shí)際優(yōu)化的效果來選擇優(yōu)化方法。

SPL  出現(xiàn)以前,因?yàn)閮?yōu)化方式的實(shí)現(xiàn)和維護(hù)都比較困難,因此試驗(yàn)動作就難以密集進(jìn)行,優(yōu)化成果不多也就是自然的了;同時(shí)因?yàn)槿狈γ芗暗跪v”數(shù)據(jù)的鍛煉,優(yōu)化經(jīng)驗(yàn)的積累也不容易,這也從另一個角度驗(yàn)證了高級數(shù)據(jù)分析師人才昂貴的現(xiàn)狀。使用高效工具的第一批人,永遠(yuǎn)是獲益大的那一群人,第一批用弓箭的,第一批用槍的,第一批用坦克的,第一個用×××的……而你就是第一批用  SPL 的程序員。程序員的龐大隊(duì)伍里分化出一支專業(yè)搞數(shù)據(jù)處理、分析的數(shù)據(jù)程序員,形成一個有獨(dú)立技能的職業(yè),這是必然的趨勢。您的職業(yè)規(guī)劃,方向選擇也要盡早有個打算,才有占領(lǐng)某一高地的可能。

       最后還要說一句,目前這個結(jié)果仍然還有優(yōu)化余地。如果再將數(shù)據(jù)壓縮存儲,還可以進(jìn)一步減少硬盤訪問時(shí)間,而數(shù)據(jù)經(jīng)過一定的排序并采用列式存儲后確實(shí)還可以再壓縮。另外,這里的集群運(yùn)算拆分成了  4  個子任務(wù),而即使配置相同的機(jī)器,也可能運(yùn)算性能不同,這時(shí)候就會發(fā)生運(yùn)算快的要等運(yùn)算慢的,最終完成時(shí)間是以計(jì)算最慢的那臺機(jī)器為準(zhǔn),如果我們能把任務(wù)拆得更細(xì)一些,就可以做到更平均的效率,從而進(jìn)一步提高計(jì)算速度。這些內(nèi)容,我們將在后面的文章繼續(xù)講述。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

當(dāng)前文章:10行代碼解決漏斗轉(zhuǎn)換計(jì)算之性能優(yōu)化-創(chuàng)新互聯(lián)
當(dāng)前路徑:http://bm7419.com/article0/dcoiio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、品牌網(wǎng)站制作域名注冊網(wǎng)站制作、動態(tài)網(wǎng)站品牌網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)