內存計算指數據事先存儲于內存,各步驟中間結果不落硬盤的計算方式,適合性能要求較高沧奴,并發(fā)較大的情況。
HANA长窄、TimesTen等內存數據庫可實現(xiàn)內存計算滔吠,但這類產品價格昂貴結構復雜實施困難纲菌,總體擁有成本較高。本文介紹的集算器同樣可實現(xiàn)內存計算疮绷,而且結構簡單實施方便翰舌,是一種輕量級內存計算引擎。
下面就來介紹一下集算器實現(xiàn)內存計算的一般過程冬骚。
一椅贱、 啟動服務器
集算器有兩種部署方式:獨立部署、內嵌部署只冻,區(qū)別首先在于啟動方式有所不同庇麦。
l 獨立部署
作為獨立服務部署時,集算器與應用系統(tǒng)分別使用不同的JVM喜德,兩者可以部署在同一臺機器上山橄,也可分別部署。應用系統(tǒng)通常使用集算器驅動(ODBC或JDBC)訪問集算服務舍悯,也可通過HTTP訪問驾胆。
n Windows下啟動獨立服務,執(zhí)行“安裝目錄esProcbinesprocs.exe”贱呐,然后點擊“啟動”按鈕丧诺。
n Linux下應執(zhí)行“安裝目錄/esProc/bin/ServerConsole.sh”。
啟動服務器及配置參數的細節(jié)奄薇,請參考: http://doc.raqsoft.com.cn/esproc/tutorial/fuwuqi.html 驳阎。
l 內嵌部署
作為內嵌服務部署時,集算器只能與JAVA應用系統(tǒng)集成馁蒂,兩者共享JVM呵晚。應用系統(tǒng)通過JDBC訪問內嵌的集算服務,無需特意啟動沫屡。
詳情參考 http://doc.raqsoft.com.cn/esproc/tutorial/bjavady.html 饵隙。
二、 加載數據
加載數據是指通過集算器腳本沮脖,將數據庫金矛、日志、WebService等外部數據讀入內存的過程勺届。
比如Oracle中訂單表如下:
訂單明細如下:
將上述兩張表加載到內存驶俊,可以使用下面的集算器腳本(initData.dfx):
A1:連接Oracle數據庫。
A2-A3:執(zhí)行SQL查詢免姿,分別取出訂單表和訂單明細表饼酿。query@x表示執(zhí)行SQL后關閉連接。函數keys可建立主鍵,如果數據庫已定義主鍵故俐,則無需使用該函數想鹰。
A4-A5:將兩張表常駐內存,分別命名為訂單和訂單明細药版,以便將來在業(yè)務計算時引用辑舷。函數env的作用是設置/釋放全局共享變量,以便在同一個JVM下被其他算法引用刚陡,這里將內存表設為全局變量,也就是將全表數據保存在內存中株汉,供其他算法使用筐乳,也就實現(xiàn)了內存計算。事實上乔妈,對于外存表蝙云、文件句柄等資源也可以用這個辦法設為全局變量,使變量駐留在內存中路召。
腳本需要執(zhí)行才能生效勃刨。
對于內嵌部署的集算服務,通常在應用系統(tǒng)啟動時執(zhí)行腳本股淡。如果應用系統(tǒng)是JAVA程序身隐,可以在程序中通過JDBC執(zhí)行initData.dfx,關鍵代碼如下:
1. com.esproc.jdbc.InternalConnection con=null;
2. try {
3. Class.forName("com.esproc.jdbc.InternalDriver");
4. con =(com.esproc.jdbc.InternalConnection)DriverManager.getConnection("jdbc:esproc:local://");
5. ResultSet rs = con.executeQuery("call initData()");
6. } catch (SQLException e){
7. out.println(e);
8. }finally{
9. if (con!=null) con.close();
10. }
這篇文章詳細介紹了JAVA調用集算器的過程 http://doc.raqsoft.com.cn/esproc/tutorial/bjavady.html
如果應用系統(tǒng)是JAVA WebServer唯灵,那么需要編寫一個Servlet贾铝,在Servlet的init方法中通過JDBC執(zhí)行initData.dfx,同時將該servlet設置為啟動類埠帕,并在web.xml里進行如下配置:
myServlet
com.myCom.myProject.myServlet
3
對于獨立部署的集算服務器垢揩,JAVA應用系統(tǒng)同樣要用JDBC接口執(zhí)行集算器腳本,用法與內嵌服務類似敛瓷。區(qū)別在于腳本存放于遠端叁巨,所以需要像下面這樣指定服務器地址和端口:
st = con.createStatement();
st.executeQuery("=callx(“initData.dfx”;[“127.0.0.1:8281”])");
如果應用系統(tǒng)非JAVA架構,則應當使用ODBC執(zhí)行集算器腳本呐籽,詳見 http://doc.raqsoft.com.cn/esproc/tutorial/odbcbushu.html
對于獨立部署的服務器锋勺,也可以脫離應用程序,在命令行手工執(zhí)行initData.dfx狡蝶。這種情況下需要再寫一個腳本(如runOnServer.dfx):
然后在命令行用esprocx.exe調用runOnServer.dfx:
D:raqsoft64esProcbin>esprocx runOnServer.dfx
Linux下用法類似宙刘,參考 http://doc.raqsoft.com.cn/esproc/tutorial/minglinghang.html
三、 執(zhí)行運算獲得結果
數據加載到內存之后牢酵,就可以編寫各種算法進行訪問悬包,執(zhí)行計算并獲得結果,下面舉例說明:以客戶ID為參數馍乙,統(tǒng)計該客戶每年每月的訂單數量布近。
該算法對應的Oracle中的SQL語句如下:
select to_char(訂單日期,'yyyy') AS 年份,to_char(訂單日期,'MM') AS 月份, count(1) AS 訂單數量 from 訂單 where客戶ID=? group by to_char(訂單日期,'yyyy'),to_char(訂單日期,'MM')
在集算器中垫释,應當編寫如下業(yè)務算法(algorithm_1.dfx)
為方便調試和維護,也可以分步驟編寫:
A1:按客戶ID過濾數據撑瞧。其中棵譬,“訂單”就是加載數據時定義的全局變量,pCustID是外部參數预伺,用于指定需要統(tǒng)計的客戶ID订咸,函數select執(zhí)行查詢。@m表示并行計算酬诀,可顯著提高性能脏嚷。
A2:執(zhí)行分組匯總,輸出計算結果瞒御。集算器默認返回有表達式的最后一個單元格父叙,也就是A2。如果要返回指定單元的值肴裙,可以用return語句
當pCustID=”VINET”時趾唱,計算結果如下:
需要注意的是,假如多個業(yè)務計算都要對客戶ID進行查詢蜻懦,那不妨在加載數據時把訂單按客戶ID排序甜癞,這樣后續(xù)業(yè)務算法中就可以使用二分法進行快速查詢,也就是使用select@b函數宛乃。具體實現(xiàn)上带欢,initData.dfx中SQL應當改成:
=A1.query("select 訂單ID,客戶ID,訂單日期,運貨費 from 訂單 order by 客戶ID")
相應的,algorithm_1.dfx中的查詢應當改成:
=訂單.select@b(客戶ID==pCustID)
執(zhí)行腳本獲得結果的方法烤惊,前面已經提過乔煞,下面重點說說報表,這類最常用的應用程序柒室。
由于報表工具都有可視化設計界面渡贾,所以無需用JAVA代碼調用集算器,只需將數據源配置為指向集算服務雄右,在報表工具中以存儲過程的形式調用集算器腳本空骚。
對于內嵌部署的集算服務器,調用語句如下:
call algorithm_1(”VINET”)
由于本例中算法非常簡單擂仍,所以事實上可以不用編寫獨立的dfx腳本囤屹,而是在報表中直接以SQL方式書寫表達式:
=訂單.select@m(客戶ID==”VINET”).groups(year(訂單日期):年份, month(訂單日期):月份;count(1):訂單數量)
對于獨立部署的集算服務器,遠程調用語句如下:
=callx(“algorithm_1.dfx”,”VINET”;[“127.0.0.1:8281”])
有時逢渔,需要在內存進行的業(yè)務算法較少肋坚,而web.xml不方便添加啟動類,這時可以在業(yè)務算法中調用初始化腳本,達到自動初始化的效果智厌,同時也省去編寫servlet的過程诲泌。具體腳本如下:
A1-B1:判斷是否存在全局變量“訂單明細”,如果不存在铣鹏,則執(zhí)行初始化數據腳本initData.dfx敷扫。
A2-A3:繼續(xù)執(zhí)行原算法。
四诚卸、 引用思維
前面例子用到了select函數葵第,這個函數的作用與SQL的where語句類似,都可進行條件查詢合溺,但兩者的底層原理大不相同卒密。where語句每次都會復制一遍數據,生成新的結果集辫愉;而select函數只是引用原來的記錄指針栅受,并不會復制數據将硝。以按客戶查詢訂單為例恭朗,引用和復制的區(qū)別如下圖所示:
可以看到,集算器由于采用了引用機制依疼,所以計算結果占用空間更小痰腮,計算性能更高(分配內存更快)。此外律罢,對于上述計算結果還可再次進行查詢膀值,集算器中新結果集同樣引用最初的記錄,而SQL就要復制出很多新記錄误辑。
除了查詢之外沧踏,還有很多集算器算法都采用了引用思維,比如排序巾钉、集合交并補翘狱、關聯(lián)、歸并砰苍。
五潦匈、 常用計算
回顧前面案例,可以看到集算器語句和SQL語句存在如下的對應關系:
事實上赚导,集算器支持完善的結構化數據算法茬缩,比如:
l GROUP BY…HAVING
l ORDER BY…ASC/DESC
l DISTINCT
l UNION/UNION ALL/INTERSECT/MINUS
與SQL的交并補不同,集算器只是組合記錄指針吼旧,并不會復制記錄凰锡。
l SELECT … FROM (SELECT …)
l SELECT (SELECT … FROM) FROM
l CURSOR/FETCH
游標有兩種用法,其一是外部JAVA程序調用集算器,集算器返回游標寡夹,比如下面腳本:
JAVA獲得游標后可繼續(xù)處理处面,與JDBC訪問游標的方法相同。
其二菩掏,在集算器內部使用游標魂角,遍歷并完成計算。比如下面腳本:
集算器適合解決復雜業(yè)務邏輯的計算智绸,但考慮到簡單算法占大多數野揪,而很多程序員習慣使用SQL語句,所以集算器也支持所謂“簡單SQL”的語法瞧栗。比如algorithm_1.dfx也可寫作:
上述腳本通用于任意SQL斯稳,
表 示 執(zhí) 行 默 認 數 據 源 ( 集 算 器 ) 的
語 句 , 如 果 指 定 數 據 源 名 稱 比 如
()表示執(zhí)行默認數據源(集算器)的SQL語句迹恐,如果指定數據源名稱比如 (orcl)挣惰,則可以執(zhí)行相應數據庫(數據源名稱是orcl的Oracle數據庫)的SQL語句。
from {}語句可從任意集算器表達式取數殴边,比如:from {訂單.groups(year(訂單日期):年份;count(1):訂單數量)}
from 也可從文件或excel取數憎茂,比如:from d:/emp.xlsx
簡單SQL同樣支持join…on…語句,但由于SQL語句(指任意RDB)在關聯(lián)算法上性能較差锤岸,因此不建議輕易使用竖幔。對于關聯(lián)運算,集算器有專門的高性能實現(xiàn)方法是偷,后續(xù)章節(jié)會有介紹拳氢。
簡單SQL的詳情可以參考: http://doc.raqsoft.com.cn/esproc/func/dbquerysql.html#db_sql_
六、 有序引用
SQL基于無序集合做運算蛋铆,不能直接用序號取數馋评,只能臨時生成序號,效率低且用法繁瑣刺啦。集算器與SQL體系不同留特,能夠基于有序集合運算,可以直接用序號取數洪燥。例如:
函數m()可按指定序號獲取成員磕秤,參數為負表示倒序。參數也可以是集合捧韵,比如m([3,4,5])市咆。而利用函數to()可按起止序號生成集合,to(3,5)=[3,4,5]再来。
前面例子提到過二分法查詢select@b蒙兰,其實已經利用了集算器有序訪問的特點磷瘤。
有時候我們想取前 N名,常規(guī)的思路就是先排序搜变,再按位置取前N個成員采缚,集算器腳本如下:
=訂單.sort(訂單日期).m(to(100))
對應SQL寫法如下:
select top(100) * from 訂單 order by 訂單日期 --MSSQL
select from (select from 訂單 order by 訂單日期) where rownum<=100 --Oracle
但上述常規(guī)思路要對數據集大排序,運算效率很低挠他。除了常規(guī)思路扳抽,集算器還有更高效的實現(xiàn)方法:使用函數top。
=訂單.top(100;訂單日期)
函數top只排序出訂單日期最早的N條記錄殖侵,然后中斷排序立刻返回贸呢,而不是常規(guī)思路那樣進行全量排序。由于底層模型的限制拢军,SQL不支持這種高性能算法楞陷。
函數top還可應用于計算列,比如擬對訂單采取新的運貨費規(guī)則茉唉,求新規(guī)則下運貨費最大的前100條訂單固蛾,而新規(guī)則是:如果原運貨費大于等于1000,則運貨費打八折度陆。
集算器腳本為:
=訂單.top(-100;if(運貨費>=1000,運貨費*0.8,運貨費))
七艾凯、 關聯(lián)計算
關聯(lián)計算是關系型數據庫的核心算法,在內存計算中應用廣泛坚芜,比如:統(tǒng)計每年每月的訂單數量和訂單金額览芳。
該算法對應Oracle的SQL語句為:
select to_char(訂單.訂單日期,'yyyy') AS 年份,to_char(訂單.訂單日期,'MM') AS 月份斜姥,sum(訂單明細.單價*訂單明細.數量) AS 銷售金額鸿竖,count(1) AS 訂單數量
from 訂單明細 left join 訂單 on 訂單明細.訂單ID=訂單.訂單ID
group by to_char(訂單.訂單日期,'yyyy'),to_char(訂單.訂單日期,'MM')
用集算器實現(xiàn)上述算法時,加載數據的腳本不變铸敏,業(yè)務算法如下(algorithm_2.dfx)
可以看到缚忧,集算器join函數與SQL join語句雖然作用一樣,但結構原理大不相同杈笔。函數join關聯(lián)形成的結果闪水,其字段值不是原子數據類型,而是記錄蒙具,后續(xù)可用“.”號表達關系引用球榆,多層關聯(lián)非常方便。
A2:分組匯總禁筏。
計算結果如下:
關聯(lián)關系分很多類持钉,上述訂單和訂單明細屬于其中一類:主子關聯(lián)。針對主子關聯(lián)篱昔,只需在加載數據時各自按關聯(lián)字段排序每强,業(yè)務算法中就可用歸并算法來提高性能始腾。例如:
=join@m(訂單明細:子表,訂單ID;訂單:主表,訂單ID)
函數join@m表示歸并關聯(lián),只對同序的兩個或多個表有效空执。
集算器的關聯(lián)計算與RDB不同浪箭,RDR對所有類型的關聯(lián)關系都采用相同的算法,無法進行有針對性的優(yōu)化辨绊,而集算器采取分而治之的理念奶栖,對不同類型的關聯(lián)關系提供了不同的算法,可進行有針對性的透明優(yōu)化门坷。
除了主子關聯(lián),最常用的就是外鍵關聯(lián)拜鹤,常用的外鍵表(或字典表)有分類框冀、地區(qū)、城市敏簿、員工明也、客戶等。對于外鍵關聯(lián)惯裕,集算器也有相應的優(yōu)化方法温数,即在數據加載階段事先建立關聯(lián),如此一來業(yè)務算法就不必臨時關聯(lián)蜻势,性能因此提高撑刺,并發(fā)時效果尤為明顯。另外握玛,集算器用指針建立外鍵關聯(lián)够傍,訪問速度更快。
比如這個案例:訂單表的客戶ID字段是外鍵挠铲,對應客戶表(客戶ID冕屯、客戶名稱、地區(qū)拂苹、城市)安聘,需要統(tǒng)計出每個地區(qū)每個城市的訂單數量。
數據加載腳本(initData_3.dfx)如下:
A4:用函數switch建立外鍵關聯(lián)瓢棒,將訂單表的客戶ID字段浴韭,替換為客戶表相應記錄的指針。
業(yè)務算法腳本如下(algorithm_3.dfx)如下
加載數據時已經建立了外鍵指針關聯(lián)脯宿,所以A1中的“客戶ID”表示:訂單表的客戶ID字段所指向的客戶表記錄矿瘦,“客戶ID.地區(qū)”即客戶表的地區(qū)字段伴找。
腳本中多處使用“.”號表達關聯(lián)引用脐恩,語法比SQL直觀易懂,遇到多表多層關聯(lián)時尤為便捷搀继。而在SQL中,關聯(lián)一多如同天書翠语。
上述計算結果如下:
八叽躯、 內外混合計算
內存計算雖然快,但是內存有限肌括,因此通常只駐留最常用点骑、并發(fā)訪問最多的數據,而內存放不下或訪問頻率低的數據谍夭,還是要留在硬盤黑滴,用到的時候再臨時加載,并與內存數據共同參與計算紧索。這就是所謂的內外混合計算袁辈。
下面舉例說明集算器中的內外混合計算。
案例描述:某零售行業(yè)系統(tǒng)中珠漂,訂單明細訪問頻率較低晚缩,數據量較大,沒必要也沒辦法常駐內存∠蔽#現(xiàn)在要將訂單明細與內存里的訂單關聯(lián)起來荞彼,統(tǒng)計出每年每種產品的銷售數量。
數據加載腳本(initData_4.dfx)如下:
業(yè)務算法腳本(algorithm_4.dfx)如下:
A2:執(zhí)行SQL待笑,以游標方式取訂單明細鸣皂,以便計算遠超內存的大量數據。
A3:將訂單表轉為游標模式暮蹂,下一步會用到寞缝。
A4:關聯(lián)訂單明細表和訂單表。函數joinx與join@m作用類似椎侠,都可對有序數據進行歸并關聯(lián)第租,區(qū)別在于前者對游標有效措拇,后者對序表有效我纪。
A5:執(zhí)行分組匯總。
九丐吓、 數據更新
數據庫中的物理表總會變化浅悉,這種變化應當及時反映到共享的內存表中,才能保證內存計算結果的正確券犁,這種情況下就需要更新內存术健。如果物理表較小,那么解決起來很容易粘衬,只要定時執(zhí)行初始化數據腳本(initData.dfx)就可以了荞估。但如果物理表太大咳促,就不能這樣做了,因為初始化腳本會進行全量加載勘伺,本身就會消耗大量時間跪腹,而且加載時無法進行內存計算。例如:某零售巨頭訂單數據量較大飞醉,從數據庫全量加載到內存通常超過5分鐘冲茸,但為保證一定的實時性,內存數據又需要5分鐘更新一次缅帘,顯然轴术,兩者存在明顯的矛盾。
解決思路其實很自然钦无,物理表太大的時候逗栽,應該進行增量更新,5分鐘的增量業(yè)務數據通常很小失暂,增量不會影響更新內存的效率祭陷。
要實現(xiàn)增量更新,就需要知道哪些是增量數據趣席,不外乎以下三種方法:
方法A:在原表加標記字段以識別兵志。缺點是會改動原表。
方法B:在原庫創(chuàng)建一張“變更表”宣肚,將變更的數據記錄在內想罕。好處是不動原表,缺點是仍然要動數據庫霉涨。
方法C:將變更表記錄在另一個數據庫按价,或文本文件Excel中。好處是對原數據庫不做任何改動笙瑟,缺點是增加了維護工作量楼镐。
集算器支持多數據源計算,所以方法B往枷、C沒本質區(qū)別框产,下面就以B為例更新訂單表。
第一步错洁,在數據庫中建立“訂單變更表”秉宿,繼承原表字段,新加一個“變更標記”字段屯碴,當用戶修改原始表時描睦,需要在變更表同步記錄。如下所示的訂單變更表导而,表示新增1條修改2條刪除1條忱叭。
第二步隔崎,編寫集算器腳本updatemem_4.dfx,進行數據更新韵丑。
A1:建立數據庫連接仍稀。
A2:將內存中的訂單復制一份,命名為訂單cp埂息。下面過程只針對訂單cp進行修改技潘,修改完畢再替代內存中的訂單,期間訂單仍可正常進行業(yè)務計算千康。
A3:取數據庫訂單變更表享幽。
A4-B5:取出訂單變更表中需刪除的記錄,在訂單cp中找到這些記錄拾弃,并刪除值桩。
A6-B6:取出訂單變更表中需新增的記錄,在訂單cp中追加豪椿。
A7-B9:這一步是修改訂單cp奔坟,相當于先刪除再追加。也可用modify函數實現(xiàn)修改搭盾。
A10:將修改后的訂單cp常駐內存咳秉,命名為訂單。
A11-A12:清空“變更表”鸯隅,以便下次取新的變更記錄澜建。
上述腳本實現(xiàn)了完整的數據更新,而實際上很多情況下只需要追加數據蝌以,這樣腳本還會簡單很多炕舵。
腳本編寫完成后,還需第三步:定時5分鐘執(zhí)行該腳本跟畅。
定時執(zhí)行的方法有很多咽筋。如果集算器部署為獨立服務,與Web應用沒有共用JVM徊件,那么可以使用操作系統(tǒng)自帶的定時工具(計劃任務或crontab)奸攻,使其定時執(zhí)行集算器命令(esprocx.exe或esprocx.sh)。
有些web應用有自己的定時任務管理工具庇忌,可定時執(zhí)行某個JAVA類舞箍,這時可以編寫JAVA類,用JDBC調用集算器腳本皆疹。
如果web應用沒有定時任務管理工具,那就需要手工實現(xiàn)定時任務占拍,即編寫JAVA類略就,繼承java內置的定時類TimerTask捎迫,在其中調用集算器腳本,再在啟動類中調用定時任務類表牢。
其中啟動類myServle4為:
1. import java.io.IOException;
2. import java.util.Timer;
3. import javax.servlet.RequestDispatcher;
4. import javax.servlet.ServletContext;
5. import javax.servlet.ServletException;
6. import javax.servlet.http.HttpServlet;
7. import javax.servlet.http.HttpServletRequest;
8. import javax.servlet.http.HttpServletResponse;
9. import org.apache.commons.lang.StringUtils;
10. public class myServlet4 extends HttpServlet {
11. private static final long serialVersionUID = 1L;
12. private Timer timer1 = null;
13. private Task task1;
14. public ConvergeDataServlet() {
15. super();
16. }
17. public void destroy() {
18. super.destroy();
19. if(timer1!=null){
20. timer1.cancel();
21. }
22. }
23. public void doGet(HttpServletRequest request, HttpServletResponse response)
24. throws ServletException, IOException {
25. }
26. public void doPost(HttpServletRequest request, HttpServletResponse response)
27. throws ServletException, IOException {
28. doGet(request, response);
29. }
30. public void init() throws ServletException {
31. ServletContext context = getServletContext();
32. // 定時刷新時間(5分鐘)
33. Long delay = new Long(5);
34. // 啟動定時器
35. timer1 = new Timer(true);
36. task1 = new Task(context);
37. timer1.schedule(task1, delay 60 1000, delay 60 1000);
38. }
39. }
定時任務類Task為:
11. import java.util.TimerTask;
12. import javax.servlet.ServletContext;
13. import java.sql.*;
14. import com.esproc.jdbc.*;
15. public class Task extends TimerTask{
16. private ServletContext context;
17. private static boolean isRunning = true;
18. public Task(ServletContext context){
19. this.context = context;
20. }
21. @Override
22. public void run() {
23. if(!isRunning){
24. com.esproc.jdbc.InternalConnection con=null;
25. try {
26. Class.forName("com.esproc.jdbc.InternalDriver");
27. con =(com.esproc.jdbc.InternalConnection)DriverManager.getConnection("jdbc:esproc:local://");
28. ResultSet rs = con.executeQuery("call updatemem_4()");
29. }
30. catch (SQLException e){
31. out.println(e);
32. }finally{
33. //關閉數據集
34. if (con!=null) con.close();
35. }
36. }
37. }
38. }
十窄绒、 綜合示例
下面,通過一個綜合示例來看一下在數據源多樣崔兴、算法復雜的情況下彰导,集算器如何很好地實現(xiàn)內存計算:
案例描述:某B2C網站需要試算訂單的郵寄總費用,以便在一定成本下挑選合適的郵費規(guī)則敲茄。大部分情況下位谋,郵費由包裹的總重量決定,但當訂單的價格超過指定值時(比如300美元)堰燎,則提供免費付運掏父。結果需輸出各訂單郵寄費用以及總費用。
其中訂單表已加載到內存秆剪,如下:
郵費規(guī)則每次試算時都不同赊淑,因此由參數“pRule”臨時傳入,格式為json字符串仅讽,某次規(guī)則如下:
[{"field":"cost","minVal":300,"maxVal":1000000,"Charge":0},
{"field":"weight","minVal":0,"maxVal":1,"Charge":10},
{"field":"weight","minVal":1,"maxVal":5,"Charge":20},
{"field":"weight","minVal":5,"maxVal":10,"Charge":25},
{"field":"weight","minVal":10,"maxVal":1000000,"Charge":40}]
上述json串表示各字段在各種取值范圍內時的郵費陶缺。第一條記錄表示,cost字段取值在300與1000000之間的時候洁灵,郵費為0(免費付運)组哩;第二條記錄表示,weight字段取值在0到1(kg)之間時处渣,郵費為10(美元)伶贰。
思路:將json串轉為二維表,分別找出filed字段為cost和weight的記錄罐栈,再對整個訂單表進行循環(huán)黍衙。循環(huán)中先判斷訂單記錄中的cost值是否滿足免費標準,不滿足則根據重量判斷郵費檔次荠诬,之后計算郵費琅翻。算完各訂單郵費后再計算總郵費,并將匯總結果附加為訂單表的最后一條記錄柑贞。
數據加載過程很簡單方椎,這里不再贅述,即:讀數據庫表钧嘶,并命名為“訂單表”棠众。
業(yè)務算法相對復雜,具體如下:
A1:解析json,將其轉為二維表闸拿。集算器支持多數據源空盼,不僅支持RDB,也支持NOSQL新荤、文件揽趾、webService。
A2-A3:查詢郵費規(guī)則苛骨,分為免費和收費兩種篱瞎。
A4:新增空字段postage。
A5-D8:按兩種規(guī)則循環(huán)訂單表痒芝,計算相應的郵費俐筋,并填入postage字段。這里多處用到流程控制吼野,集算器用縮進表示校哎,其中A5、B7為循環(huán)語句瞳步,C6闷哆、D8跳入下一輪循環(huán),B5单起、C7為判斷語句
A9:在訂單表追加新紀錄抱怔,填入匯總值。
計算結果如下:
至此嘀倒,本文詳細介紹了集算器用作內存計算引擎的完整過程屈留,同時包括了常用計算方法和高級運算技巧〔饽ⅲ可以看到灌危,集算器具有以下顯著優(yōu)點:
l 結構簡單實施方便,可快速實現(xiàn)內存計算碳胳;
l 支持多種調用接口勇蝙,應用集成沒有障礙;
l 支持透明優(yōu)化挨约,可顯著提升計算性能味混;
l 支持多種數據源,便于實現(xiàn)混合計算诫惭;
l 語法敏捷精妙翁锡,可輕松實現(xiàn)復雜業(yè)務邏輯。
關于內存計算夕土,還有個多機分布式計算的話題馆衔,將在后續(xù)文章中進行介紹。?