數據準備 ——報表開發中的深層次問題

前言

現在企業的報表開發大部分都使用報表工具完成,成熟的報表工具提供了豐富的顯示設置、圖表類型、導出打印等功能可以簡化報表開發,非常方便。但在實際報表開發中還是經常碰到一些非常棘手的深層次問題,即使是已經熟練使用報表工具的開發老手也會很撓頭。

爲什麼有了報表工具還會出現這些問題呢?

報表開發,看起來就是將數據按照指定格式的表格或圖形呈現出來,這也是報表工具一直以來很擅長的環節。但是,原始數據經常並不適合直接呈現,需要先做一些複雜的處理,這就是數據準備環節。

從報表工具的眼光上看,數據準備屬於報表之外的事情,可以堂而皇之地拒絕處理。但是,拒絕不等於不存在,這個工作總還要做。沒有好的工具,目前報表的數據準備還處於比較原始的硬編碼階段,上千行的 SQL、幾百 K 的存儲過程和大量的 JAVA 代碼充斥在報表之後。

落後的工具必然導致低下的生產效率,會嚴重拖累整個報表開發的進程,也就出現了前述的“撓頭”現象。再由於大多數報表工具的不重視,這個問題遲遲還沒有被解決的跡象。

報表數據準備之於報表有如樹根之於大樹,如果根本得不到解決,在枝葉上花多少精力都是白費。

這種深層次問題在報表開發初期一般很少碰到,但隨着報表開發的深入這些問題會接連不斷地出現了。

我們在這裏收集整理了一批此類問題,以供報表開發者參考。內容比較多,建議先收藏再慢慢看。

目錄

1. 零編碼製作報表可能嗎?

2. 爲什麼說當前報表開發的工作量主要在數據源環節?

3. 用存儲過程和 JAVA 寫報表數據源有什麼弊端?

4. 什麼是報表的多樣性數據源問題?

5. 報表的性能問題是怎樣產生的?

6. 什麼是大報表?

7. 中間表是什麼?和報表有什麼關係?會帶來怎樣的問題?

8. 報表熱切換是什麼意思?

9. 報表沒完沒了是個什麼狀況?

1. 零編碼製作報表可能嗎?

要回答這個問題,首先要明確啥程度算“零編碼”?
以 Excel 爲例,如果把寫 Excel 公式(包括複雜一些的)看做零編碼;而把寫 Excel VBA 看做編碼的話,

報表開發是可以零編碼的!

但是,這有個前提:在數據(集)準備好的情況下才可以零編碼!

爲什麼這麼說?
我們知道報表開發主要分兩個階段:
第一階段是爲報表準備數據,也就是把原始數據通過 SQL/ 存儲過程加工成數據集;
第二階段是使用已準備的數據編寫表達式做報表呈現。在報表工具提供的 IDE 裏可視化地畫出報表樣式,然後再填入一些把數據和單元格綁定的表達式就可以完成報表呈現了,雖然表達式可能比較複雜,但相對硬編碼要簡單得多(Excel 公式和 VBA 的關係)。所以說這個階段是能做到“零編碼”的。

那報表數據準備怎麼辦?
很遺憾,這個階段沒法零編碼,一直以來只能硬編碼,想想我們報表裏寫的嵌套 SQL、存儲過程、JAVA 程序就知道了。爲什麼報表工具發展這麼多年報表呈現已經完全工具化而報表數據準備的手段還這樣原始呢?因爲這個階段太複雜了,不僅涉及計算邏輯的算法實現,還涉及報表性能(要知道大部分報表性能問題都是數據準備階段引起的)。

那報表數據準備是不是沒辦法了呢?
雖然不能做到零編碼,但可以朝着簡單化的方向努力,將數據準備階段也工具化,這樣可以使用工具提供的便利來簡化報表數據準備階段的工作,從而進一步簡化報表的開發。

那怎麼實現報表數據準備工具化?
要實現這個目標並不容易,像上面提到要考慮的內容有點多,大體來說數據準備工具至少要滿足這幾方面:
1. 具備完備的計算能力
說的有點拗口,掰開了其實在說既然在工具裏做數據計算,那得讓我什麼都能算吧,不能原來 SQL/JAVA 寫的放到這裏就不行了,該有的計算方法和類庫都應該有,最好用起來還比較簡單(比原來硬編碼難就沒意義了),專業的說法叫:計算體系是完備的;

2. 支持熱切換
這點是相對 JAVA 來說的,通過數據準備工具生成的算法應該是解釋執行的,不能每次改完報表還要重啓應用,即時修改即時生效;

3. 具備多源混算能力
通過數據準備工具可以同時連接多種數據源(RDBMS、NoSQL、TXT、Excel、Hadoop、HTTP、ES、Kafka 等等)進行計算,混合計算,這個數據源讀個表、那個數據源加載個文件,兩部分數據可以 join 到一起混算。現在我們的數據源太多了,報表常常會跨數據源取數,支持了異構源混算以後,原來還要考慮諸如數據是不是先入到一個庫裏的事情就不用管了,那叫一個清爽;

4. 高性能
直接簡化數據準備的工作還不夠,實現再簡單跑不快也不行。所以,還要高性能,至少不能比原來跑的慢吧,大家都是講道理的人;

以上是我們認爲數據準備工具應該具備的能力,其他還有一些能力不是特別重要,但如果有最好了。包括:
* 有沒有易用的編輯調試環境,可以很方便地調試算法;
* 爲了更快能不能並行計算
* 有沒有標準接口可以讓其他程序或工具調用
等等,實際要用的時候照着這些特點去找就行了,有益無害。

說了這麼多,總結來說,“零編碼製作報表”的確更像一句口號,沒法真正做到,但可以不斷努力接近這個目標,求其上得其中嘛。

擴展閱讀:

【數據蔣堂】第 43 期:報表開發的現狀
報表提效資料彙總(體系結構和性能優化)

2. 爲什麼說當前報表開發的工作量主要在數據源環節?

我們知道,報表開發主要有兩個階段。
第一階段:數據準備。將原始數據加工成報表需要的結果集(數據源);
第二階段:數據呈現。根據已準備的結果集(數據源)編寫表達式將數據以表格或圖形方式呈現。

這兩個階段雖然處於同一報表開發過程,但實現方式卻大不相同。

通常原始數據距離報表“能用”還相去甚遠,通過 SQL/JAVA/ 存儲過程等編碼方式準備報表可用的數據源是第一階段的目標,過程中可能涉及複雜的數據處理過程,因而這個階段會牽扯較多的精力,佔用的工作量也多。

數據源準備好後,通過報表工具來解決數據以何種方式(圖表)、何種樣式(外觀)呈現的問題,通過點選設置、編寫少量表達式就可以快速完成,實現簡單,佔用的工作量也少。

可以說,報表開發的工作量主要在數據處理(計算),數據處理後如何呈現,通過工具可以快速完成。

那是不是報表呈現階段就不涉及數據處理了呢?
並不完全是這樣。

早期報表開發
大概 2015 年以前,報表呈現方式主要以表格(或圖表混合)爲主,這時通過報表工具實現時就會涉及一定的數據計算,如分組彙總、多源分片、格間計算(同比環比)等。如下圖所示:

計算分佈在數據準備和數據呈現兩個階段,兩個階段的工作量相當(各佔 50%)。這個時期,數據準備階段可以把數據準備得“粗糙”一些,然後利用報表工具的計算能力在呈現階段將數據進一步加工成目標結果進行呈現。比如,我們在數據準備階段完成關聯過濾,再在報表呈現模板中按多個維度分組彙總;或者在呈現模板中計算同比環比。

當前報表開發
隨着報表工具的逐漸成熟,報表工具提供了更豐富的圖形(類型和效果),報表選擇圖形呈現幾乎與表格佔比相當了(佔比仍在增長)

使用圖形呈現,由於沒有“格子”,就無法利用報表工具的計算能力在呈現模板中完成數據處理,而圖形本身並不具備計算能力(不包括硬編碼),這時自然而然就要在數據準備階段將前端需要的數據完全準備好,前端工具接收數據直接圖形呈現。
圖形設置基本沒什麼工作量,而後端數據源的準備工作就佔了大頭,數據呈現和數據準備所佔的工作量佔比約爲 20%:80%。

當前報表開發的工作量主要在數據源端(數據準備階段),要提升報表開發效率,勢必要解決報表數據源準備效率問題。這也是爲什麼很多時候用上了一流的報表工具,但報表開發的工作量仍然很大的原因。

如何提升報表數據源端的開發效率,從而整體降低報表開發工作量?

借鑑報表開發的發展歷史,或許可以獲得一些思路。
早期,報表開發靠完全硬編碼,無論數據準備還是報表呈現,後來報表工具出現替代了硬編碼方式,將報表呈現階段的開發工具化,解放了報表呈現階段的人力(你可以感受一下寫代碼畫報表比用工具做麻煩多少)。

按照這個思路,用工具替代硬編碼就可以提高生產效率,將數據準備階段工具化就可以解決報表數據源的開發效率,從而進一步提升報表整體開發效率。

數據準備工具可以是獨立的,也可以包含在報表工具內部,後者要求報表工具的能力能夠延伸到數據源層面。

擴展閱讀:
【數據蔣堂】第 10 期:報表的數據計算層
如何應對報表開發中的複雜邏輯

3. 用存儲過程和 JAVA 寫報表數據源有什麼弊端?

我們在報表開發中經常會使用存儲過程準備數據,存儲過程支持分步計算,可以實現非常複雜的計算邏輯,爲報表開發帶來便利。所以,報表開發中這樣的存儲過程並不少見:

3008 行,141KB 的存儲過程,會給報表開發帶來什麼不好的影響?

1. 編輯調試性
存儲過程難以編輯調試,這樣幾千行存儲過程的開發週期往往要以周或月計,這樣會嚴重影響報表的開發效率,而業務提的報表需求似乎都“很急”。

2. 維護性
相對開發的一次性,維護的工作可能要經常做。實際業務中報表經常會修改,這種現象叫做報表業務的穩定性差。報表的數據準備邏輯變化,修改上千行的存儲過程對絕大多數報表開發人員來說都是噩夢。

有時這樣的報表會分兩撥人來做,DBA 或專業程序員負責編寫存儲過程給前端報表開發人員做報表,這樣就避免了報表開發人員寫存儲過程。但這樣報表修改的流程會變長,修改一張報表涉及多個人員之間溝通(還包括業務人員),如果負責報表前後端的兩撥人隸屬不同的團隊就更麻煩了。

3. 知識傳承
從維護性可以直接引出另一個“知識傳承”的問題。還是拿上面的報表爲例,如果一個新人要改上面的報表,你覺得他要多久能看懂存儲過程,改完報表?

當然,這個問題還涉及很多管理方面的手段,單純從技術本身來看,這樣的報表想要很好地傳承知識是很難的。

4. 安全性
對存儲過程的修改需要較高的數據庫權限,而報表經常要改就要經常操作數據庫,這對數據庫安全也是一個隱患,同樣需要強管理機制才能保障一二。

5. 移植性
現在絕大多數規定禁止使用存儲過程的原因,首當其衝的就是存儲過程沒有移植性。如果未來數據庫發生變化需要遷移,不管將來是更換數據庫類型,還是系統擴展(分表分庫),大量無法移植的存儲過程絕對是最頭疼的問題。

當然,“換庫”這件事情即使在今天仍然不會頻繁發生,但是隻要發生一次就夠受了(有國產化或系統擴展預期的就要注意了)。

6. 耦合性
從維護性、安全性和移植性看來,存儲過程會導致報表應用(前端)和數據庫(後端)緊耦合。緊耦合除了會導致前面的三個問題外,還會讓數據庫編的臃腫,影響數據庫性能。

重要的事情說好多遍,報表的業務不穩定,報表除了經常增加和修改,有時還會刪除(不用了),而爲這個報表準備的存儲過程還在數據庫裏,這時想要刪掉這個存儲過程就比較難了。

爲什麼?

因爲你不知道是不是還有其他程序在共用這個存儲過程,刪除會不會對其他程序產生影響。結果就是數據庫的存儲過程越積越多導致數據庫臃腫,而有的存儲過程還會涉及自動運行,雖然存儲過程可能不再使用,但仍然在消耗數據庫資源,長此以往數據庫性能下降就成爲必然了。

7. 多源支持
存儲過程運行在封閉的數據庫內,無法進行跨多數據源混合計算。關於多源問題,幾年前在報表開發還不顯著,那時大家都用關係庫;但現在不一樣了,同一個報表的數據可能來自多個不同類型的數據源(RDB/NoSQL/TxT/Excel/Hadoop/ES 等等),這時存儲過程就無能爲力了。

如何搞定這些問題?
有沒有辦法解決存儲過程帶來的這些問題呢?

當然有!

沒有什麼是硬編碼解決不了的!用 JAVA 替代存儲過程,脫離數據庫運行來解決上面的問題(自行搜索 SOA 和微服務理念)。存儲過程一個顯示的好處是可以分步實現報表數據準備邏輯,這個優點 JAVA 也有,甚至比存儲過程更徹底,說句文縐縐的話:JAVA 的離散性更好。

只是 JAVA 寫起來比較麻煩,對於報表開發人員來講太難了,如果還要加一個修飾詞那就是太 XX 難了。存儲過程使用的 SQL 語言非常適合做集合運算,分組彙總一句 group by 就寫出來了,反觀 JAVA 就不具備這個優點了,分組彙總可能要寫上幾十上百行纔行(類庫缺失會讓開發複雜度急劇上升,想想你爲什麼不用匯編寫程序而要用 JAVA?)。

JAVA 還有一些其他的問題也不容忽視。

不支持熱切換
JAVA 還有一個非常致命的缺點,就是不支持熱切換。報表經常要改(又來一遍),修改報表數據源以後還要重新編譯、重啓應用才能生效,對絕大多數業務系統都是不能接受的。報表講究的不僅是查詢立等可取,修改也要實時生效纔行。

報表與應用緊耦合
與使用存儲過程會導致報表與數據庫緊耦合類似,用 JAVA 準備報表數據源會導致報表模塊和應用的其他業務模塊緊耦合不宜維護。

我們知道,報表大多數情況都是作爲一個模塊集成到應用系統提供報表查詢服務,集成的方式可以是 API(jar 包)方式緊集成;也可以將報表單獨發佈成服務,通過服務調用的方式松集成,這樣報表服務器產生的任何壓力或問題都不會影響應用系統(高可用)。*API 緊集成後,由於報表數據源是 JAVA 寫的,這樣就要和主應用的代碼一起打包,無法作爲獨立的模塊維護,而未來想要拆分也基本不可能了;* 服務松集成則完全無法實現。

所以,用 JAVA 寫報表數據源雖然可行,但也不是特別理想。

那有沒有其他辦法呢?

我們比較一下存儲過程和 JAVA 的優缺點可以發現,解決問題應該是沿着繼承二者優點,改進缺點的方向進行。清晰起見,總結一下需要的點。

 

把主要的點列了一下,我們的目標就是找到支持這些點的技術手段(問號所在行)。

易開發、易維護
這注定了這些能力應該是報表工具內置的,這樣報表開發人員自己就能使用工具搞定報表開發,而不必依賴其他人或團隊;

熱切換
熱切換要求這個技術應該是解釋執行的,這樣才能做到實時修改實時生效;

支持多源
能夠對接多種不同類型的數據源進行混合計算,比如文本和數據庫表的 join;

低耦合、可移植
數據準備能力和報表呈現能力都報表內置了,自然與應用和數據源都解耦了,未來系統擴展自然毫無壓力。

這樣我們可以很容易想到在報表端增加一個計算模塊(或引入其他計算工具),來替代存儲過程或 JAVA 爲報表準備數據,這個模塊可以是由嵌入報表工具的腳本來實現,結構可以是這樣的

腳本要具備完善的計算能力(什麼計算都能算),支持多源,解釋執行允許熱切換這些能力。

這纔是報表工具的未來!

擴展閱讀:
怎樣減少報表開發中對存儲過程的依賴
如何應對報表開發中的複雜邏輯

4. 什麼是報表的多樣性數據源問題?

在報表開發早期,報表連接的數據源基本只有關係數據庫,而且經常只有一種或者只有一個數據庫。

但今天就不一樣了,數據源種類繁多,除了 RDBMS 還有
1.MongoDB、Cassandra、HBase、Redis 這些 NoSQL 數據庫;
2.TXT/CSV、Excel、JSON/XML 等文件;3.HDFS 等分佈式文件系統;
4.webService;
5.ES、Kafka 等其他數據源形式
……

當這些都成爲報表數據源,報表需要從這些數據源分別或混合取數運算進行報表呈現時,報表就出現了多樣性數據源問題。

具體是什麼樣的問題呢?

主要是兩個問題,複雜計算和多源關聯計算。

1. 複雜計算
我們知道,報表中的計算主要集中在兩處:

一處是數據準備階段。
通過 SQL/ 存儲過程 /Java 程序爲報表準備數據,這個階段可能涉及非常複雜的數據處理邏輯。這樣, 計算能力尤其是集合計算能力較強的 SQL 就比較擅長了,通過 SQL、複雜 SQL 可以完成大部分的報表數據準備任務,有些涉及較多業務邏輯的計算還可以使用存儲過程,萬不得已時用 JAVA 自定義數據源完成。

這是早期基於單一 RDBMS 開發報表時數據準備的常用手段,主要依靠 RDBMS(SQL)的計算能力來實現。

但這種方式在多樣性數據源的場景下就行不通了,因爲有的數據源根本就不支持 SQL,甚至計算能力都比較弱(如 NoSQL),或者根本就沒有計算能力(如文本),這樣,數據準備計算計算無法在這個階段實現,就要看另外一處是否可以完成了?

二處是報表呈現階段
根據第一階段已準備的數據,在報表模板中填入綁定格子的報表表達式或圖形來呈現報表是使用報表工具開發報表的常用方式。這說明報表工具具備一定的計算能力,通過表達式可以實現分組彙總、過濾、排序,複雜一些的同比環比等計算。

但是,報表工具的計算能力是有限的。不考慮性能的情況下,單純從數據源中讀取數據到報表呈現階段完成數據組織和報表呈現很多時候是做不到的,這也是爲什麼我們會在數據準備階段進行復雜數據處理的原因了。

2. 多源關聯計算
與基於單一的某個數據源進行的複雜計算不同,有時一個報表會同時連接多個數據源,多個數據源之間要混合計算,比如 MongoDB 的數據和 RDBMS 的數據關聯運算,文本和 Excel 關聯運算等。

這種跨異構數據源的關聯無法直接通過數據源自身的能力實現,只能藉助其他方法。


那報表的多樣性數據源問題如何解決呢?

方法總比問題多。目前大家普遍採用兩種方式來解決報表多樣性數據源的問題。

藉助 RDBMS
曲線救國。將多樣性數據源的數據通過 ETL 灌到關係庫中,再基於關係庫出報表,這樣就可以避免多樣性數據源的問題,轉而使用最熟悉的手段來解決。

不過,這種方式的侷限性很大。因爲之所以出現多樣性數據源,是因爲各種數據源有各自適用的場景,換句話說很多是關係庫搞不定的,所以纔會用這些數據源,比如 NoSQL 的 IO 吞吐能力很強,但計算能力較弱;文本 /Excel 文件適合做臨時存儲且不需要持久化到 DB;Webservice 則非常靈活,入庫的動作就顯得過於笨重,…

且不說多樣性數據源的數據是否能轉換到關係庫中,由於要經過 ETL 的過程,數據的實時性如何保證?數據量較大時除了 ETL 慢,RDBMS 的容量是否夠用?查詢性能是否滿足報表查詢要求?等等這些問題都是這種方式要面對的。

採用這種方式經常是“不得已”,因爲解決某類問題上了其他數據源,結果因爲出報表又要用回關係庫,也不知道隱含了多少辛酸。

JAVA 硬編碼
通過 RDBMS 來解決報表多樣性數據源的問題有這樣那樣的問題,那直接硬編碼怎麼樣?通過 JAVA 硬編碼對接多樣性數據源爲報表準備數據,畢竟硬編碼想幹啥就幹啥。

這種方式我們之前有分析過,除了編碼難、維護難的問題(報表開發人員基本搞不定),還存在無法熱切換(JAVA 是編譯型語言)和與業務應用緊耦合(代碼要跟業務應用主程序一起打包部署)這些問題。這是我們之前聊過的: 用存儲過程和 JAVA 寫報表數據源有什麼弊端?

硬編碼似乎也不理想。

事實上,我們只需要增強報表工具的計算能力就能解決這個問題。

1. 首先,提供多樣性數據源的支持,通過報表工具可以連接這些數據源,要實現這一步相對簡單;

2. 其次,提供複雜計算支持,讓所有的複雜計算都能在報表中完成。實現手段可以是強化呈現端的計算能力,通過報表格子表達式就能完成這些複雜計算。不過,對於綁定格子的計算(狀態式計算)想要支持複雜計算並不容易,在呈現端要兼顧數據處理和數據呈現很多計算就做不了了,而且呈現格會帶有很多呈現屬性(字體、顏色、邊框等等),帶着這些屬性計算會佔用過多內存,嚴重影響計算性能。


很難在報表呈現格表達式中完成複雜計算(功能和性能都不滿足)

可以想到的另外一種方式是在報表中增加計算模塊用來專門做多樣性數據源混合計算,其位置與原來爲報表準備數據的 SQL 和 JAVA 相當,只不過是內嵌在報表中,屬於報表自身的能力,結構大概是這樣

 

在原有的基礎上增加了計算模塊,計算模塊可以通過可編程計算腳本實現,但對這個腳本能力有要求:

1. 提供多樣性數據源訪問接口
能直接對接多樣性數據源是基礎,種類越豐富對多樣性數據源問題解決得越充分;

2. 支持複雜計算
當數據源自身不具備強計算能力時,通過腳本可以完成報表數據準備階段的複雜計算邏輯,腳本提供豐富的計算類庫,可以很方便地實現這類計算,最好比 SQL 和 JAVA 都簡單;

3. 支持多源關聯
可以跨異構數據源關聯計算,這是也解決報表多樣性數據源問題需要具備的重要能力;

4. 支持熱切換
腳本修改後可以實時生效,而不會像 JAVA 一樣需要重啓應用。

當我們遇到報表多樣性數據源問題需要選擇報表開發技術時不妨沿着這個方向考量一下。

擴展閱讀:
【數據蔣堂】第 10 期:報表的數據計算層
在報表中直接使用多樣性數據源

5. 報表的性能問題是怎樣產生的?

報表性能是總也避不開的話題,報表作爲 OLAP(在線聯機分析)中的主要應用場景,無論從涉及數據的寬度(表數量),還是數據的廣度(查詢範圍)都可能非常巨大;而且在報表中還經常伴隨非常複雜的數據處理邏輯,這些都會影響報表的運行速度。而服務器環境、數據庫環境、JDBC 效率、網絡環境、客戶端環境這些也都都跟報表性能密切相關。

報表性能可能跟很多因素有關,非常複雜。這裏我們試着從報表運行的各個階段來分析報表性能問題產生的主要原因及其應對方法。未盡之處,歡迎討論。

我們知道報表運行主要分報表解析、數據準備、數據傳輸、報表計算和報表生成 5 個階段。除了報表解析是引擎加載解析模板,還未開始運算外,其他 4 個階段(示意圖中黃色的部分)均可能引起性能問題。

我們在分析報表性能問題時一定要先定位是哪個階段引起的,抓主要矛盾。定位的方法也很簡單,就是分析報表運行日誌,很多報表工具都會輸出各個階段的運算時間,看看哪個階段耗時最長,就是問題發生的主要階段了。

1. 數據準備
報表數據準備是指從數據源中讀取數據並將數據組織成報表可用的結果集的過程。在報表中往往是以數據集的形式存在,可以通過 SQL、存儲過程或 JAVA 實現。

數據準備並不是簡單地將數據源的原始數據取出就結束了,而是會伴隨一些計算過程,有些還很複雜,可以想想一下平時我們開發報表時編寫的 SQL 絕大多數情況下都不是簡單的 select * from tbl。如果這個 SQL 比較複雜(由於大多數情況都是使用 SQL 準備數據,所以這裏我們僅以 SQL 爲例,其他數據源可以參考 SQL 數據集的解決辦法)運行較慢,就會導致報表變慢。

這個階段在數據庫中運行,本質上跑的快慢都跟報表無關,但好的報表工具是可以干預優化這個過程的(後面再說)。

數據準備階段慢,可以先試着優化 SQL,但 SQL 優化往往復雜度較高,所以也經常採用預計算的方式進行性能優化(這裏不討論硬件升級、數據庫擴容等物理優化手段)。

(1) 優化 SQL
我們知道複雜 SQL(關聯多、嵌套多)是比較難優化的。數據庫的透明化機制讓寫 SQL 時不用關心底層的執行順序,由數據庫優化器自動執行,這樣可以簡化編寫 SQL 的難度,但過於透明的機制讓我們很難干預 SQL 的執行路徑,也就很難優化 SQL 了。

(2) 預計算
SQL 無法優化或優化效果不理想時,通過預計算可以提升報表數據準備效率。將報表需要的結果集事先加工出來存儲到中間表中,報表查詢時直接讀取加工好的結果集,這樣就可以節省大量計算時間,從而提升報表性能。預計算本質是用空間換時間的手段。

不過,預計算的缺點也非常明顯,你不妨先思考一下有哪些問題?

主要的問題有兩個:時間問題和空間問題。

時間問題
預計算需要“事先”加工,其本質是一個 ETL 過程,這樣就會引起兩個時間問題,數據的實時性和預計算的時間成本。

報表基於預計算的結果查詢只能查詢預計算時點以前的數據,無法查詢預計算到當前時間的數據,這樣就導致了數據時效性差,不適合數據實時性要求高的業務場景(比如交易系統)。

預計算往往會放到業務空閒的階段進行,比如前一天夜裏到第二天上班前,通常還要預留一些時間容錯(跑失敗了要重新跑),這樣可能留給預計算的時間也就 5、6 個小時,這個時長基本是固定不變的,而數據規模、業務複雜度、報表數量都會不斷增長,未來極有可能會引發預計算跑不完而影響業務使用的問題。

空間問題
預計算的結果是要落地的,往往會存儲在數據庫物理表中,這樣的表多了會佔用大量數據庫空間,引起數據庫容量問題;另外這樣的表多了還會帶來管理上的問題。

預計算的應用範圍很窄,而 SQL 又比較難優化,還有什麼其他優化手段嗎?

(3) 藉助其他高性能計算引擎
在數據庫內搞不定報表數據準備階段優化時可以引入其他高性能計算引擎,這在業內並不少見,但幾乎所有技術都是採用獨立存儲 + 分佈式架構來輸出高性能的,這對報表應用架構(主要是數據庫)的改變就有些大了,難度很大。

比較簡單直接的方式是在報表內部就能提供改變 SQL 執行路徑的手段,並且可以改善那些顯著低效的 SQL 算法(比如 topN),這樣就可以在不改變應用架構的情況下實現數據準備階段的優化。

這就要求報表工具提供數據準備階段的計算能力,既可以分步執行 SQL(指定執行順序),還可以改善算法效率。其表現形式可以是一種內置在報表工具中的計算腳本,腳本具備分步計算和強計算能力。


報表工具計算模塊提供可干預 SQL 執行路徑能力和高效的強計算能力


2. 數據傳輸
報表通過 JDBC 接口訪問數據庫讀取所需數據時,如果數據量比較大或者數據庫 JDBC 性能較差(要知道各種數據庫的 JDBC 效率是不同的)會導致數據傳輸時間過長,導致報表變慢。

由於我們沒法改變數據庫的驅動,我們只能在報表層面想辦法。一個可行的辦法是通過並行取數來提速。

(1) 並行取數
通過建立多個數據庫連接(這時要求數據庫相對空閒),採用多線程的方式同時讀取報表所需數據,可能是同一個表,也可能是多個表關聯計算後的結果,這樣數據傳輸的時間理論上就會縮短到原來的 1/n(n 是線程數),從而提升報表性能。

那麼這種並行取數實現起來難度如何呢?

因爲目前大部分報表工具不支持並行取數,要想通過並行來加速數據傳輸只能自己使用 JAVA 硬編碼來做。我們曾經討論過在報表中使用 JAVA 硬編碼準備數據的弊端,而編寫並行程序難度又提升了一級,要知道,並行以後還要歸併(merge)各個線程的計算結果,merge 不是簡單地縱向拼接就完了,有時還涉及分組和順序。

使用支持並行取數的報表工具
比較簡單有效的方式是使用支持並行取數的報表工具,報表開發人員不需要考慮數據資源衝突、結果歸併等複雜問題就可以簡單實現。

(2) 異步傳輸
當數據量較大時,可以通過異步機制將數據分批返回給報表,報表接收部分數據後就立刻呈現,後臺同時進行不間斷的數據傳輸,這樣就可以提升報表的呈現速度。

異步傳輸需要考慮很多方面,不建議自己硬編碼實現,最好使用支持異步傳輸功能的報表工具,簡單快捷。

3. 報表計算
數據傳輸完成後,報表引擎會根據已準備數據和報表表達式運算報表,這個階段也是非常容易出現性能問題的。

性能問題常見於多數據集報表。

現在很多報表工具都提供了多數據集能力,允許開發者在一個報表中建立多個數據集,這樣可以分別組織數據,尤其當數據來自不同數據庫時,多數據集尤其方便。但這種方式爲報表開發帶來便利的同時卻會帶來很嚴重的性能問題。

我們知道,多數據集的關聯是在報表單元格的表達式中完成的,類似這樣 ds2.select(ID==ds1.ID),報表引擎在解析這個表達式時會按照順序遍歷的方式完成關聯,即從 ds2 中拿出一條記錄,到 ds1 中遍歷,查找 ID 相同的記錄;然後再拿第二條再去遍歷查找;…
這個運算複雜度是平方級的,數據量小的時候沒什麼影響,數據量稍大時性能就會急劇下降。

另外,報表單元格還帶有大量呈現屬性(顏色、字體、邊框等),帶着這些屬性運算也會拖慢報表的運行速度。所以,對於數據量較大的多個數據集,最好在數據準備階段使用更高效的關聯算法完成關聯。

在數據準備階段完成關聯

將多數據集關聯轉移到數據準備階段完成,通過 SQL 就可以實施更加高效的關聯,只是 SQL 只能基於單一數據庫操作,如果數據來自多個數據源時就不靈了。這時,最好報表工具在數據準備階段還提供諸如 HASH JOIN 等高性能關聯算法,以便滿足異構數據來源時的關聯運算。HASH JOIN 算法可以整體地看待幾個數據集,效率比報表工具採用的過濾式關聯要高得多,幾千行規模時幾乎是零等待。

4. 報表生成
報表計算完成後會生成引擎會將計算結果以 HTML 的形式輸出,生成最終要呈現報表。這個階段的性能問題主要有兩方面。

(1) 頁面渲染慢
當報表中添加了大量的呈現效果(隔行異色、背景圖、警戒色等)時,頁面渲染的速度就會變慢。減少過多的報表效果使用可以提升渲染速度;如果這些效果是必須的,那就要比拼使用的報表工具的能力了。

另外,如果報表單頁的內容過大也會影響頁面呈現的速度。對於比較高或者比較寬的報表呈現時建議採用分頁方式輸出,從而提升頁面呈現效率。

(2)客戶端環境差
總體上是跟用戶端環境有關係,比如網絡問題、設備問題等,遇到時需要具體分析。

通過了解報表運行各階段可能出現的性能問題,以及對應解決辦法,有些我們可以硬編碼或藉助其他技術來解決,有些還需要報表自身來提升。最直接的方式是使用能夠提供這些能力的報表工具,這個工具大概要具備這些特性才能幫助我們有效解決報表性能問題。

1. 支持分步
這樣可以有效優化 SQL 執行路徑,提升數據準備效率;

2. 內置強計算能力和高效算法
能夠改善原有低效的算法,提高數據計算效率;
支持 HASH JOIN 等高性能關聯算法,以減少在呈現模板中進行多數據集關聯;

3. 支持並行取數
能夠通過多線程並行方式提升數據傳輸速度效率;

4. 支持異步讀取
提供異步取數異步呈現機制,加速大數據量報表的呈現效率;

5. 支持多數據源
提供多樣性數據源接口,並能夠進行多數據源關聯運算(同 2)。

擴展閱讀:
【數據蔣堂】第 3 期:功夫都在報表外 - 漫談報表性能優化
性能優化是個手藝活
如何分析報表性能問題

6. 什麼是大報表?

實際業務中有些報表比較“大”,查詢出的報表數據行數可以達到幾千萬甚至上億,這類行數很多的報表通常被成爲“大報表”。大報表大部分情況下是清單明細報表,少量是分組報表。

大報表查詢通常不會採用一次性取出所有記錄再交給前端呈現的方式,因爲這樣要等很久,用戶體驗極差;而且報表服務器內存也喫不消。

常見的方式是通過分頁來呈現大報表,一次只取一小部分數據,取數結束後立刻交給前端呈現,當頁碼變化時再取出相應頁數的數據,這樣可以加快報表呈現速度,用戶幾乎沒有等待感。

具體如何實現呢?有幾種方式。

1. 數據庫分頁
業界最常用的做法是使用數據庫分頁來實現,具體來講,就是利用數據庫提供的返回指定行號範圍內記錄的語法。界面端根據當前頁號計算出行號範圍(每頁顯示固定行數)作爲參數拼入 SQL 中,數據庫就會只返回當前頁的記錄,從而實現分頁呈現的效果。

主要藉助關係數據庫自身的能力,每種數據庫實現上會有所差異,Oracle 可以使用 rownum,mysql 則可以 limit,具體實現網上有很多資料這裏不再贅述。

數據庫分頁有沒有什麼不足?任何技術都有其應用範圍,數據分頁的問題主要集中在以下 4 點。

(1)翻頁時效率較差
用這種辦法呈現第一頁一般都會比較快,但向後翻頁時,所使用的取數 SQL 會被再次執行,並且將前面頁涉及的記錄跳過。對於有些沒有 OFFSET 關鍵字的數據庫,就只能由界面端自行跳過這些數據(取出後丟棄),而像 ORACLE 還需要用子查詢產生一個序號才能再用序號做過濾。這些動作都會降低效率,浪費時間,前幾頁還感覺不明顯,但如果頁號比較大時,翻頁就會有等待感了。

(2) 可能出現數據不一致
用這種辦法翻頁,每次按頁取數時都需要獨立地發出 SQL。這樣,如果在兩頁取數之間又有了插入、刪除動作,那麼取的數反映的是最新的數據情況,很可能和原來的頁號匹配不上。例如,每頁 20 行,在第 1 頁取出後,用戶還沒有翻第 2 頁前,第 1 頁包含的 20 行記錄中被刪除了 1 行,那麼用戶翻頁時取出的第 2 頁的第 1 行實際上是刪除操作前的第 22 行記錄,而原來的第 21 行實際上落到第 1 頁去了,如果要看,還要翻回第 1 頁才能看到。如果還要基於取出的數據做彙總統計,那就會出現錯誤、不一致的結果。

爲了克服這兩個問題,有時候我們還會用另一種方法,用 SQL 遊標從數據庫中取數,在取出一頁呈現後,但並不終止這個遊標,在翻下一頁的時候再繼續取數。這種方法能有效地克服上述兩個問題,翻頁效率較高,而且不會發生不一致的情況。不過,絕大多數的數據庫遊標只能單向從前往後取數,表現在界面上就只能向後翻頁了,這一點很難向業務用戶交代,所以很少用這種辦法。

當然,我們也可以結合這兩種辦法,向後翻頁時用遊標,一旦需要向前翻頁,就重新執行取數 SQL。這樣會比每次分頁都重新取數的體驗好一些,但並沒有在根本上解決問題。

(3) 無法實現分組報表
除了清單報表,有時我們還要呈現大數據量的分組報表,報表包含分組、分組彙總及分組明細數據。我們知道,按頁取數按照翻頁每次讀取固定條數(一頁或幾頁)記錄,這樣根本無法保證一次性讀取一個完整分組,而分組呈現、分組彙總都要求基於整組數據來操作,否則就會出錯。

(4) 無法使用其他數據源
數據庫分頁是藉助關係數據庫自身的能力,而對於非關係數據庫就不靈了。試想一下,NoSQL 怎麼用 SQL 分頁,文本怎麼做分頁?

報表的多樣性數據源話題我們曾經討論過,在數據規模迅速膨脹的今天,基於非關係數據庫出報表已經非常普遍。


除了數據庫分頁,還有其他更好的方式嗎?

2. 硬編碼實現
我們發現基於數據庫的分頁方式強依賴數據源,無法滿足其他數據源類型的需要,也就是與數據庫緊耦合的。我們需要一個低耦合數據源的實現方式以應對多樣性數據源場景,還能同時解決效率、準確性和分組呈現問題。

按照主流的解題思路,可以通過編碼方式實現這個目標,實現思路大概是這樣:

基於數據庫時,
把取數和呈現做現兩個異步線程,取數線程發出 SQL 後就不斷取出數據後緩存到本地存儲中,呈現線程根據頁數計算出行數到本地緩存中去獲取數據顯示。這樣,只要已經取過的數據就能快速呈現,不會有等待感,還沒取到的數據需要等待一下也是正常可理解的;而取數線程只涉及一句 SQL,在數據庫中是同一個事務,也不會有不一致的問題。這樣,兩個問題都能得到解決。不過這需要設計一種可以按行號隨機訪問記錄的存儲格式,不然要靠遍歷把記錄數出來,那反應仍然會很遲鈍。

基於非數據庫時,
同樣設置取數和呈現兩個異步線程,取數時通過文件遊標(或其他數據源提供的分批取數接口)分批讀取數據並緩存到本地,呈現階段則與上述方式完全一致。

然後再利用報表工具開放的接口和前端報表進行交互,完成報表的分頁呈現。

做分組報表時,
就需要一次性取出完整分組,在代碼裏進行分組彙總,並將彙總結果插入結果集一併返回給前端報表進行呈現,同時還要設置分組記錄標誌位,以便前端報表在呈現時能夠爲分組行設置不同的顯示效果(如加粗、標紅)。

實現後的效果類似下面這樣:gif

取數線程不停地取數緩存,呈現線程從緩存中讀取數據呈現,總頁數不斷變化

通過上面的描述,可以看到自己硬編碼雖然可以實現,但複雜度很高。除了多線程編程,還要考慮緩存數據存儲形式、文件遊標、分組讀取與彙總,此外借助其他報表工具進行呈現時還要對方開放足夠靈活的接口,…,這些都是挑戰。

而且我們只考慮了呈現,如果還要導出 Excel 怎麼辦?如果還要打印怎麼辦?畢竟報表既然能查就應該能導出,能打印。這些需求在金融、製造行業都是真實存在的。


3 使用支持大報表的報表工具
如果前端使用報表工具開發報表,選用一個直接支持大報表呈現、導出、打印的報表工具則更爲直接。工具實現了兩個異步線程的大報表機制,同時解決上面我們提到的問題,這些方面都封裝好直接使用。

大數據時代要支持大報表,這應該是報表工具必備能力。

擴展閱讀:
大清單報表應當怎麼做?
海量清單與分組報表的實現
如何實現海量數據清單和分組報表
秒級展現的百萬級大清單報表怎麼做

7. 中間表是什麼?和報表有什麼關係?會帶來怎樣的問題?

在數據庫中有一類用於保存中間計算結果的物理表,通常被稱爲“中間表”。中間表主要跟 OLAP(在線聯機分析)業務有關,產生的原因主要有以下幾方面。

中間表來源

1. 計算邏輯複雜
在 OLAP(報表或查詢)業務中,有些計算邏輯很複雜,每次都從頭寫會導致報表開發過於繁瑣,而且有的計算用 SQL 很難寫出來。這時會採用中間表事先計算好,再基於預計算的中間結果開發報表。

計算邏輯複雜常見於報表業務中,以固定報表最爲常見;多維分析則比較少見。

2. 查詢性能差
當查詢涉及的數據量很大或者計算邏輯很複雜時查詢性能會很差。爲了提升查詢性能,增強用戶體驗,通常會把彙總結果實現計算出來存儲在中間表中。基於預彙總的中間表查詢速度會快很多。

在實際業務中,大部分提升查詢速度的中間表也都是爲報表服務的。

3.ETL 過程轉存
ETL 過程也會產生中間表。ETL 過程中常常會涉及到數據庫的數據,正常的 ETL 過程應當是 E、T、L 這三個步驟逐步進行,也就是先清洗轉換之後再加載進數據庫,最後在數據庫中的只是合理的結果數據。但是,E(清洗)和 T(轉換)這兩個步驟中會涉及到大量數據計算,而在數據庫外實施這些計算很不方便,所以實際情況就會是把涉及到的所有數據都先加載進來然後再進行清洗和轉換,ETL 過程變成了 ELT 甚至 LET。事先要加載的這些數據在關係數據庫中也必須以表的形式存儲,這就使數據庫中增加了許多並非最終需要的中間表。

如果觀察一下這些跑批任務,你會發現 ETL 任務很多都是爲報表業務服務的。

4. 多樣性數據源混合計算
另一種情況是多樣性數據源造成的,這也是爲數據呈現(報表查詢)服務的。現代應用中的數據呈現經常會涉及數據庫外的數據,目前一般的做法是把庫外數據定時導入到數據庫中,然後就能和數據庫內的數據一起運算產生報表,否則很難實現數據庫內外的數據的混合運算。這當然也會讓數據庫中多了一些表,而且,有些互聯網上取過來的數據常常是多層的 json 或 XML 格式,在關係數據庫中還要建立多個關聯的表來存儲,會進一步加劇中間表過多的問題。

通過列舉的 4 箇中間表產生的主要原因,我們發現一個共同點:中間表大部分情況都是爲報表服務的。我們也知道,實際業務中的報表數量非常多,而且報表業務業務不穩定經常會新增修改報表,這會導致中間表數量不斷增多。

中間表會帶來哪些問題

中間表是一把雙刃劍,提供很多便利的同時也會帶來一些問題。

我們曾在一個運營商的報表系統中,發現了一個讓人喫驚的現象。在 DB2 數據倉庫中,有兩萬多個數據庫表!經過深入瞭解發現,真正的原始數據表只有幾百張,剩下的大量的數據庫表都是爲查詢和報表服務的中間表。

像這種系統經過幾年乃至十幾年的運行,數據庫中的中間表越來越多,甚至出現這種上萬個的情況並不少見。大量中間錶帶來的直接困擾是數據庫存儲空間不夠用,面臨頻繁的擴容需求。中間表對應的存儲過程、觸發器等等需要佔用數據庫的計算資源,也會造成數據庫的擴容壓力。

中間表過多還會帶來管理方面的問題,對於成千上萬張中間表想要梳理清楚恐怕是一件非常頭疼的事情。

那麼,是不是可以清理掉一些不用的中間表?一般的結論都是:搞不動。數據庫中的中間表是不同程序員製作的,有的是綜合查詢系統使用,有的是報表系統使用。中間表之間還存在交叉引用,有些程序員看到有別人生成的中間表就直接使用了。有時候一些查詢報表已經廢棄不用了,但是對應的中間表沒人敢刪,因爲不知道刪掉之後會影響其他什麼查詢或者報表。

很多情況下,項目組只好爲了越來越多的中間表去擴容數據庫。但是數據庫的擴容成本太昂貴了:不管是換更強的服務器(縱向擴容),還是增加數據庫服務器的節點(橫向擴容),都不便宜。

總結來說,中間表會帶來管理、容量和性能三方面的問題。

如何解決中間表的問題?

可以很容易想到的方式是使用庫外文件存儲中間表數據,這樣中間表脫離了數據庫就不會對數據庫再產生影響。但是,在實際應用中這種辦法卻很少使用。爲什麼呢?

我們知道,中間表是要再計算的,基於中間表查詢的報表還要進行數據過濾,有的還要再次彙總,還可能涉及關聯計算,這些操作在數據庫裏通過 SQL 完成很簡單。但是文件沒有計算能力,要實施這些計算只能硬編碼,用 JAVA 來做,使用 JAVA 來做集合運算又非常麻煩,遠沒有數據庫(SQL)方便。所以採用文件存儲中間表的方式使用並不廣泛,主要是由實現複雜度過高導致的。

那還有什麼好的方式呢?

使用支持文件源的報表工具
既然中間表大部分是爲報表服務的,而通過將中間表外置到文件中可以解決中間錶帶來的這些問題,那麼直接使用支持文件源的報表工具是否就可以了呢?

答案是肯定的。

我們來看一下要實現這個目標,報表工具要具備哪些能力?

(1) 豐富的計算類庫
要解決文本計算難的問題,報表工具要提供豐富的計算類庫,除了能完成所有數據處理任務(都能算)以外,還要實現簡單,這是基礎,太複雜了沒法用;

(2) 多樣性數據源接口
支持多樣性數據源以後,就可以不用通過數據庫中轉直接讀取多樣性數據源數據出報表;

(3) 異構數據源混合計算能力
提供多樣性數據源接口後,還要能夠進行異構源間的混合計算,這樣就可以徹底解決掉多樣性數據源帶來的中間表問題;

(4) 高性能
除了實現簡單以外,計算性能也要有保障,從而滿足前端報表查詢的性能需要。

具體實現上可以在報表工具中增加計算模塊來對接“庫外中間表”,結構類似這樣

其中庫外中間數據文件可以採用本地文件,也可以使用網絡或分佈式文件系統,或其他數據源。

要解決中間表問題,需要報表工具強化自身的計算能力才能實現!

擴展閱讀:
【數據蔣堂】第 14 期:計算封閉性導致臃腫的數據庫
【數據蔣堂】第 15 期:開放的計算能力爲數據庫瘦身
有效減少數據庫中間表的報表開發方法
爲什麼會有這麼多中間表?

8. 報表熱切換是什麼意思?

熱切換(Hot Swap)是指在系統不停機的情況下更換系統部件,在報表業務中則是指在不重啓報表及相關應用的情況下完成對報表的維護(新增、修改、刪除),冷切換則恰好相反。

報表業務很不穩定,業務開展的過程中會刺激出更多查詢統計需求,如果每次需求實現後都要等系統空閒(往往是非工作時間)時重啓系統才能讓修改後的報表生效,那將會對業務使用造成非常大影響(延遲)。因此在需求經常會變化的報表業務中,熱切換顯得尤其重要。

事實上,在報表業務中要實現熱切換並不難,只要在開發報表時選擇合適的技術手段,避免使用諸如 JAVA 這樣的編譯型語言(雖然重寫類加載器可以變相實現熱加載,但過於繁瑣,也沒從根本上解決問題)即可。

但在實際應用中我們卻看到大量使用 JAVA 來開發報表,造成無法熱切換的情況。爲什麼會這樣?

報表業務有兩個特點,一個是報表業務很複雜。報表是一項負責綜合性統計的任務,將原始數據加工成業務需要的報表形式要走很長的路,不僅數據處理邏輯繁瑣,報表呈現樣式也千奇百怪,所謂中國式複雜報表。

另一個特點是嵌入使用。報表經常會作爲應用系統的一部分(模塊)嵌入到系統中使用。

現在報表呈現格式方面的問題已經基本得到解決,以國內成熟報表工具爲主導的一批國產軟件可以很好地解決中國式複雜報表呈現的問題,而且這些工具大多數採用解釋執行機制,報表模板修改後可以實時生效。

但數據準備階段的問題卻一直沒有解決,複雜的數據處理和業務邏輯控制讓開發人員不得采用硬編碼手段來實現。由於大部分應用系統都採用 JAVA 開發,既然報表是系統的一部分,業務邏輯又這麼複雜,硬編碼的首選自然是 JAVA 了。這是導致在報表中大量使用 JAVA 導致無法熱切換的原因。

那麼如何解決這個問題?

將報表的數據準備也工具化!

早期,報表開發完全靠硬編碼的時代,大家也普遍採用 JAVA 來“畫”報表,這樣勢必存在無法熱切換的問題,後來報表工具出現解決了呈現模板的熱切換問題。所以,要徹底實現報表熱切換,將數據準備也工具化是一個可行的方向。

然而,報表的數據準備和報表呈現兩個階段分別使用不同工具會造成報表開發和維護上的困擾,因此,最好的方式是增強報表工具自身的計算能力,將兩個階段都融入可以解釋執行的報表模板中,從而徹底實現報表熱切換。

我們知道,報表工具本身具備一定的計算能力,在呈現模板的單元格中可以編寫分組、求和、比值等表達式,但由於格子的限制很難處理複雜的數據邏輯,這也是爲什麼在報表中需要數據集(JAVA/SQL)來準備數據的原因。

一個可行的辦法是在報表工具中增加可解釋執行的計算腳本(模塊)用於數據準備,這樣在計算模塊中可以實現任意複雜的數據處理邏輯,同時解釋執行的腳本內置在報表模板中實現熱切換。

報表計算模塊(腳本)要具備這樣一些能力:1. 解釋執行
這是實現熱切換的基本能力,數據準備和報表呈現內置在一個報表模板中,採用解釋執行機制運行;

2. 計算體系完善
能實現所有的數據處理任務,原來在 JAVA 中的報表數據準備計算在腳本中都應該能完成,具備豐富的集合運算類庫;

3. 易開發維護
實現上要足夠簡單,這樣報表開發人員就能直接搞定數據準備和報表呈現,而不再依賴其他專業程序員;同時在一個報表模板裏更易於維護。

參考資料:
如何應對報表開發中的複雜邏輯

9. 報表沒完沒了是個什麼狀況?

可以先想一下自己的部門或項目組是否面臨這些問題:
1. 投入很多技術力量做報表,卻還是疲於應付
2. 用了高端報表工具和敏捷 BI,卻還是不夠用
3. 技術高手用來做報表,感覺很浪費 4. 對於頻繁多變的報表需求,需要低成本應對方案

專門用於統計分析的報表業務有一個特點,就是業務穩定性非常差。在業務開展過程中會催生很多新的查詢需求,而且已實現的查詢需求還會經常變化,這就造成了沒完沒了的報表。所以經常會有這麼一段對話

報表沒完沒了是需求使然,無法規避,只能適應,而目前主要的是問題是普遍缺少一種低成本的方案來適應沒完沒了的報表。

爲什麼報表開發成本一直居高不下?

我們知道,報表工具的主要作用是將報表呈現階段的開發工具化,使用報表工具可以將原本需要硬編碼的工作通過工具來提高生產效率;但報表開發的另一個階段:數據準備,卻仍然在使用原始的硬編碼方式處理,有時我們要編寫非常複雜的 SQL、存儲過程和 JAVA 程序,這樣的工作通常要依賴高水平的技術人員才能完成。

另外,採用過於原始手段會帶來報表維護困難,複雜的代碼無論從編寫還是修改都比較困難,這樣又會加劇報表開發成本居高不下!

除了技術原因外,還有應用結構和團隊管理方面的因素也會造成報表開發的成本居高不下。在許多應用系統中,報表是耦合在其中的一些功能,而業務系統的技術環境很複雜,這就迫使報表開發人員也要熟悉這些東西,也就很難把報表開發人員和業務系統的開發人員分開,業務系統上線之後,開發人員仍然要繼續堅守開發報表,很難把這項工作轉交給客戶方的運維人員。

開發過程中的有效溝通也會影響工作量。我們經常會發現這樣的現象:業務人員提出報表需求,等技術人員做好後才發現某些概念術語的理解有誤,統計口徑不一致,結果並不是業務人員想要的,然後也只能重做,甚至反覆多次才能正確實現,嚴重浪費開發資源。


如何解決報表開發成本過高?如何低成本地應對沒完沒了的報表?

可以嘗試通過如下五個步驟來解決這些問題。

 

1. 引入報表工具

先把最容易解決的問題解決掉,通過引入專業的報表工具解放報表數據呈現階段的人力,報表工具可以完成包括中國式複雜報表在內的各種圖表呈現。選擇成熟且性價比高的工具是這個階段的主要目標。

2. 增加計算模塊

引入報表工具後,解放了數據呈現階段的人力,降低了一定的成本,但佔主要部分的報表數據準備階段仍未解決。

數據準備階段的特點是:1. 編碼困難,沒有普適的工具 2. 對人員要求高,需要高水平技術人員完成 3. 實現週期過長,難以適應多變的報表需求 4. 硬編碼耦合性高,依賴數據庫和 JAVA 都都會造成緊耦合 5. 運維過於複雜,修改維護成本提高

這時,我們需要沿着第一步的方向繼續前進,將數據準備階段也工具化。比較好的方式是
在報表工具中增加用於數據準備的計算模塊,將原來使用 SQL/ 存儲過程 /JAVA 實現的數據準備算法,全部通過計算模塊完成,使得報表開發徹底工具化,簡化開發,降低成本。

計算模塊的能力可以通過腳本來實現,在腳本中內置各種豐富的計算類庫,讓報表開發人員獨立就能完成數據準備階段的工作,從而全面接管報表的開發,而不再依賴其他專業程序員。

這裏尤其要注意,報表計算模塊需要具備這樣一些能力。

1. 易於編碼
包含豐富的計算類庫,可以很方便地完成各類數據計算任務;必要時還能提供可視化的編輯調試環境進一步簡化這個階段的開發;

2. 支持熱切換
計算模塊需採用解釋執行機制,這樣可以很好地和前端報表呈現模板結合在一起,報表修改後可以實時生效,無需重啓整個應用;

3. 支持多樣性數據源
提供 RDB、NoSQL、文本、Excel、hadoop 等多樣性數據源接口,報表直接基於多言行數據源開發,也可以進行混合計算(如 Excel 和 RDB 的表 join);

4. 高性能
計算模塊的計算性能不能低於原來 SQL/JAVA 的計算性能,最好高於原來的實現方式。

報表開發全面工具化以後,就可以獲得更高的報表開發效率,進一步降低報表開發成本。

3. 獨立報表模塊

報表開發全面工具化以後,就可以着手梳理應用結構,獨立報表模塊,從而將報表模塊從業務系統中解耦出來。

1. 梳理數據源
首先梳理數據源,將報表業務相關的數據源都整理出來,以後的報表開發只需要和這些數據源打交道;同時爲下一步剝離報表業務做準備。

2. 剝離報表業務
將數據源和業務應用中與報表相關的數據準備(複雜 SQL、存儲過程、中間彙總表、自定義 JAVA 類)全部轉移到報表工具中實現;同時將這些內容從數據庫和業務應用中清除,完成報表業務剝離。

3. 獨立報表模塊
報表業務完全剝離後,在報表工具中實現的報表在物理上就可以獨立於數據源和業務模塊存儲,不再與業務系統和數據源緊密耦合,完全獨立報表模塊

4. 調整應用結構
獨立報表模塊後,報表模塊與業務模塊共享數據存儲;應用系統調用報表模塊爲用戶輸出報表結果;同時解釋執行報表模塊支持熱切換,即改即用,無需重啓應用。

獨立報表模塊以後,報表就可以單獨修改和維護,清晰的應用結構會進一步降低報表開發成本。

4. 建設報表團隊

報表模塊獨立後,建設專門的報表開發團隊,不再需要高成本應用程序員或 DBA 參與報表開發,進一步降低報表開發成本。

報表開發人員就無需應對紛繁複雜的應用環境,更無需關係應用架構,只需根據已有架構開發報表就可以了。半年以上工作經驗的技術人員就能勝任(其實,理工科應屆畢業生就可以了,甚至高中學歷都行)。

簡化報表開發後,還可以嘗試將持續的報表開發工作移交給客戶方的本地運維人員,這樣不僅能降低開發商的成本,還能提高對客戶的響應速度。

5. 完善溝通機制

最後就是建立有效的溝通機制,減少交流中的誤解。

一個簡單可行的辦法就是建立企業內部的報表知識庫,技術形式上搞一個論壇即可。把以前做過的報表收集到論壇上加以評註,並提供搜索功能。

當有了新的報表需求時,可以搜索歷史庫中是否曾經做過類似的報表(出現過相同的業務術語)。歷史報表中保留有相關代碼或公式,而這些形式化的信息不會有歧義,這就能幫助新的開發人員正確理解業務術語。

甚至許多報表部分還可以被直接複用,在人員變動時也可以最大限度地保證業務知識的繼承。


應對報表沒完沒了是一個長期的過程,是涉及技術、管理等諸多方面的綜合性事務。當這些步驟完成以後就可以全方位應對沒完沒了的報表了。

擴展閱讀:
應對報表沒完沒了的五個步驟
一勞永逸地“解決”沒完沒了的報表開發

結語

報表做不完,都賴數據源;報表開發快與慢,數據準備是關鍵。

通過提升報表工具的數據準備能力可以解決大部分報表開發過程中的深層問題, 從而通過改進生產工具來提升生產效率。這樣,在選擇報表工具時,除了能滿足呈現方面的需求外,也要關注工具本身的數據準備能力,自然是越強越好。

關於當前主流報表工具在這方面的能力,可以搜索“十大活躍報表深度點評”,其中有深入的評論。

我們還將持續關注報表開發中遇到的各類難題,對這方面感興趣的同學可以再搜索“乾學院”上的免費商業智能課程,進一步瞭解和學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章