券是促銷系統(tǒng)的必備東西,將券導(dǎo)出Excel文件,也是常見的業(yè)務(wù)。如果但是導(dǎo)出20w券需要十幾分鐘,那是不可忍受的。
導(dǎo)出Excel文件的步驟如下:
- 根據(jù)預(yù)設(shè)的數(shù)量,生成一批券。
- 將券以Excel文件導(dǎo)出。
我們需要優(yōu)化每一個步驟!
如何高效地生成一批券?
直寫db是不可接受的。通過這種方式生成1k張券問題不大,但想要快速生成1w張、20w張,就需要更好的手段。那就是分批次異步生成。
改進(jìn)過程如下:
1、通過類似snowflake算法生成20w券號。
2、將20w個券號,分成50份,通過rabbitmq以異步的形式出去,委托集群上的所有機(jī)器進(jìn)行消費(fèi),將券寫入數(shù)據(jù)庫。
注意事項(xiàng):
- 如果券還未完全生成好,提友好提示用戶券正在生成。
- 可以使用redis鎖,防止券被重復(fù)導(dǎo)出。
- 消費(fèi)端做好重復(fù)消費(fèi)的處理。
有了券之后,如何高效導(dǎo)出為Excel文件?
答案時采用并發(fā)寫入。20w張券可以寫到4個sheet中,每個sheet最多可容納63356條件記錄(包括標(biāo)題)。一個sheet對應(yīng)一個線程,這樣就有4個線程同時寫入,性能應(yīng)該會有所提升。
并發(fā)寫入要注意2個問題:
- 如何計算sheet的數(shù)量?如何計算每個sheet需要記錄的coupon范圍?
引入Map<Integer,Integer>數(shù)據(jù)結(jié)構(gòu)。map的長度,就是sheet的數(shù)量。key代表coupon的開始位置,value代表結(jié)束位置。 - 如果控制并發(fā)寫入?
使用線程池來執(zhí)行任務(wù)。
使用CountDownLatch協(xié)調(diào)子任務(wù)。每個子線程完成任務(wù)后,更新CountDownLatch,直至為0,則主線程就可以繼續(xù)執(zhí)行。
在此方案上進(jìn)行壓測:導(dǎo)出在小數(shù)據(jù)量的券,時間是縮短了,但導(dǎo)出到20w張券的時間還是很長。
經(jīng)過分析,使用HSSFWorkbook來導(dǎo)出Excel時,HSSFWorkBook會把所有記錄留在內(nèi)存中,建議使用SXSSFWorkbook來解決大數(shù)據(jù)導(dǎo)出的問題。它可以限制內(nèi)存保留的記錄行數(shù),超過的記錄會寫入硬盤。適合于適合追加數(shù)據(jù)的場景,不適合于頻繁修改的場景。
將poi到3.9版本后,替換HSSFWorkBook為SXSSFWorkbook,新的測試結(jié)果為:在小數(shù)據(jù)量的情況下,響應(yīng)時間是秒級。導(dǎo)出20w張券大約花了10秒。這個時間是可以接受。
總結(jié)
分而治之是提高性能的常見手段,既可以通過mq來達(dá)到目的,也可通過線程池。