資料查詢業務中,有時會碰到資料量很大的清單報表。由於使用者輸入的查詢條件可能很寬泛,因此會從資料庫中查出幾百上千萬甚至過億行的記錄,常見的包括銀行流水記錄,物流明細等。呈現時如果等著把這些記錄全部檢索出來再生成報表,那會需要很長時間,使用者體驗自然會非常惡劣。而且,報表一般採用記憶體運算機制,大多數情況下記憶體裡也裝不下這麼多資料。所以,我們一般都會使用分頁呈現的方式,盡量快速地呈現出第一頁,然後使用者可以隨意翻頁顯示,每次只顯示一頁,也不會造成記憶體溢位。
傳統分頁呈現的實現,一般都會使用資料庫的分頁機制來做,利用資料庫提供的返回指定行號範圍內記錄的語法。介面端根據當前頁號計算出行號範圍(每頁顯示固定行數)作為引數拼入 sql 中,資料庫就會只返回當前頁的記錄,從而實現分頁呈現的效果。
不過,這樣做會有兩個問題:
1. 翻頁時效率較差
用這種辦法呈現第一頁一般都會比較快,但向後翻頁時,所使用的取數 sql 會被再次執行,並且將前面頁涉及的記錄跳過。對於有些沒有 offset 關鍵字的資料庫,就只能由介面端自行跳過這些資料(取出後丟棄),而像 oracle 還需要用子查詢產生乙個序號才能再用序號做過濾。這些動作都會降低效率,浪費時間,前幾頁還感覺不明顯,但如果頁號比較大時,翻頁就會有等待感了。
2. 可能出現資料不一致
用這種辦法翻頁,每次按頁取數時都需要獨立地發出 sql。這樣,如果在兩頁取數之間又有了插入、刪除動作,那麼取的數反映的是最新的資料情況,很可能和原來的頁號匹配不上。例如,每頁 20 行,在第 1 頁取出後,使用者還沒有翻第 2 頁前,第 1 頁包含的 20 行記錄中被刪除了 1 行,那麼使用者翻頁時取出的第 2 頁的第 1 行實際上是刪除操作前的第 22 行記錄,而原來的第 21 行實際上落到第 1 頁去了,如果要看,還要翻回第 1 頁才能看到。如果還要基於取出的資料做彙**計,那就會出現錯誤、不一致的結果。
當然,我們也可以結合這兩種辦法,向後翻頁時用游標,一旦需要向前翻頁,就重新執行取數 sql。這樣會比每次分頁都重新取數的體驗好一些,但並沒有在根本上解決問題。
下面介紹的潤幹報表方案,提供的大報表功能可以支援海量清單報表的秒級查詢。在這個方案中,取數和呈現採用兩個非同步執行緒,取數執行緒發出 sql 後不斷取出資料快取到本地,由呈現執行緒從本地快取中獲取資料進行顯示。這樣,已經取出並快取的資料就能快速呈現,不再有等待感;而取數執行緒所涉及的 sql,在資料庫中保持同乙個事務,也不會有不一致的問題,前面提到的兩個問題全部得以完美解決。
同時,借助集檔案儲存格式,報表還可以按行號隨機訪問記錄,而不用每次通過遍歷查詢資料。也就是說,這種儲存格式支援跳轉到任意頁訪問,從而極大地改善了使用者體驗。不過,由於採用了非同步機制,頁面端顯示的總頁數和總記錄數會隨著取數過程不斷變化。
大清單報表執行原理:
需要注意的是,大清單報表中用到的非同步機制和集檔案儲存都是在集算器的基礎上實現的,因此該功能需要「整合集算器」功能支援,並不包含在潤幹報表基礎版中。
下面通過舉例來說明潤幹海量大清單報表(以下簡稱:大報表)的開發使用過程。
首先來看一種最基礎的大報表,即報表資料**於資料庫的情況。例子中我們需要根據日期範圍查詢訂單表的交易資訊,由於資料規模較大,因此需要使用大清單報表呈現。
與普通報表開發一樣,設定引數、準備資料集、繪製報表模板。
報表引數為查詢日期起止:
資料集根據引數查詢訂單表 sql:
報表模板:
與普通報表不同,需要在潤幹報表屬性(報表 - 報表屬性)中設定「大資料集名稱」,指向資料集 ds1,直接利用 sql 完成非同步取數。
設定完成後,在報表設計器 ide 中即可瀏覽報表:
與普通報表發布類似,大清單報表也通過 jsp 以 tag-lib 的方式發布。
"
needscroll=""
params=""
exceptionpage="/reportjsp/myerror2.jsp"
scrollwidth="100%"
scrollheight="100%"
rownumperpage="20"
fetchsize="1000"
needimporteasyui="no"
/>
" needscroll="" params="" exceptionpage="/reportjsp/myerror2.jsp" scrollwidth="100%" scrollheight="100%" rownumperpage="20" fetchsize="1000" needimporteasyui="no" />
web 端呈現效果:
可以注意到,右上角的頁碼和總記錄條數隨著非同步執行緒不斷讀取資料而不斷變化。
除了展現,在潤幹報表中還支援對大清單報表匯出 excel 和列印。
當海量資料**非 rdb 時,由於無法利用資料庫分頁,因此無法通過 sql 實現非同步大報表。針對這一問題,潤幹報表的大報表方案中,採用了兩階段非同步執行緒,其中由集算器定義取數執行緒負責從非 rdb 資料來源取數並快取資料,再由呈現執行緒負責讀取快取並分頁展現。
下面以檔案資料為例,說明非 rdb 資料來源的大報表開發過程。例子中的衛星資料以檔案(csv)方式儲存,資料規模較大,是典型的非 rdb 資料來源。現在我們要按照日期查詢某日風速、溫度等明細資訊。
首先,我們借助潤幹報表的集算器資料集讀取檔案資料,並為報表返回游標。集算器 spl 指令碼如下:a
=file(「source.csv」).cursor@t(;,",")
=a1.select(時間 == d_time)
return a2
a1 建立檔案游標;
a2 在游標執行時對資料進行過濾(此時游標尚未執行,資料並未取出);
a3 返回游標過濾結果,為報表提供資料。
將 spl 指令碼儲存為 bigreport-file.dfx,並在報表中作為資料集引入:
接下來,我們根據所準備的資料製作報表模板:
同時設定大資料集:
然後,將做好的報表發布到 web 端:
是不是很簡單? 這個例子顯示了在開發報表時如果涉及海量非 rdb 資料,潤幹報表可以借助集算器對多種資料來源型別的支援能力,實現大報表開發與呈現。
上面介紹了開發大報表該有的正確姿勢,但任何功能都不是萬能的,使用大報表也有需要注意的地方:
1、不排序
大報表的資料集都比較大,如果在意響應時間(誰會不在意呢),那麼應該盡量不對資料集進行全表排序(注意我說的是全表排序),畢竟,等排完序再呈現,時間已經過去很久了……
2、不適合高併發場景
大報表採用非同步機制,將資料分批載入到記憶體再交給前端呈現,減少了記憶體占用,但同時增加了 cpu 和磁碟 i/o 負載,併發高時 cpu 和硬碟可能成為瓶頸從而影響呈現效果,因此大報表不適合高併發的場景。
MySQL 對於千萬級的大表要怎麼優化?
很多人第一反應是各種切分 我給的順序是 第一優化你的sql和索引 第二加快取,memcached,redis 第三以上都做了後,還是慢,就做主從複製或主主複製,讀寫分離,可以在應用層做,效率高,也可以用三方工具,第三方工具推薦360的atlas,其它的要麼效率不高,要麼沒人維護 第四如果以上都做了還...
MySQL 對於千萬級的大表要怎麼優化?
對大資料的資料庫管理優化的總結 常用的 優化sql 突出快字,使完成操作的時間最短 1 用索引提高效率 2 選擇有效率的表名順序,及資料結構及欄位 3 使用decode函式可以避免重複掃瞄相同記錄或重複連線相同的表 4 刪除重複記 5 過內部函式提高sql效率 讀寫分離 操作不在乙個表裡完成 1 主...
MySQL 對於千萬級的大表要怎麼優化?
很多人第一反應是各種切分 我給的順序是 第一優化你的sql和索引 第二加快取,memcached,redis 第三以上都做了後,還是慢,就做主從複製或主主複製,讀寫分離,可以在應用層做,效率高,也可以用三方工具,第三方工具推薦360的atlas,其它的要麼效率不高,要麼沒人維護 第四如果以上都做了還...