前言
最近遇到個(gè)需求,需要定時(shí)任務(wù)處理大批量數(shù)據(jù)的導(dǎo)入匕荸、更新和刪除爹谭,數(shù)據(jù)量每次在十萬(wàn)級(jí)左右,極少情況有百萬(wàn)每聪、千萬(wàn)級(jí)旦棉,所以肯定不能直接一次性處理,內(nèi)存和數(shù)據(jù)庫(kù)都頂不住药薯。因此我用lambda表達(dá)式寫(xiě)了一個(gè)鏈?zhǔn)教幚淼姆椒ㄈ缦隆?/p>
private void batchHandle(int total, Function<Integer, List<?>> queryFunction, Function<List<?>, Void> handleFunction) {
for (int i = 0; i < total; i++) {
queryFunction.andThen(handleFunction).apply(i);
}
}
簡(jiǎn)介
方法一共有三個(gè)參數(shù)绑洛,第一個(gè)參數(shù)total是本次任務(wù)需要處理的數(shù)據(jù)總量,我會(huì)先用count的sql查出來(lái)童本,然后傳遞給第一個(gè)function作為入?yún)⒄嫱停鼤?huì)返回一個(gè)List<?>,結(jié)合方法里的for穷娱,這里就能拿到分批后的單批量數(shù)據(jù)绑蔫,然后將list傳給第二個(gè)function运沦,對(duì)單批數(shù)據(jù)做統(tǒng)一的處理邏輯(增刪改查)。
踩坑
- 最初我的設(shè)想是每批2000條數(shù)據(jù)配深,直接拿到數(shù)據(jù)然后操作入庫(kù)携添,寫(xiě)出來(lái)的第一版代碼在我小批量的測(cè)試數(shù)據(jù)下也沒(méi)出現(xiàn)問(wèn)題,但是一旦數(shù)據(jù)量上升篓叶,這里的單次耗時(shí)就會(huì)成倍累加烈掠,尤其是數(shù)據(jù)量到了千萬(wàn)級(jí)別,哪怕處理邏輯非常簡(jiǎn)單缸托,單次耗時(shí)1秒總計(jì)也有83分鐘左敌,而且這段時(shí)間一直占用著資源還是非常夸張的俐镐。
- 況且第一個(gè)function批量處理肯定需要對(duì)全部數(shù)據(jù)進(jìn)行分頁(yè)矫限,那么隨著limit數(shù)字越來(lái)越大,分頁(yè)查詢的耗時(shí)會(huì)越來(lái)越多佩抹,并且業(yè)務(wù)表里主鍵是uuid且沒(méi)有合適的排序字段叼风。
- 再其次,兩個(gè)function里和數(shù)據(jù)庫(kù)交互的部分需要做緩存棍苹,否則每一批都需要重新查詢咬扇,對(duì)數(shù)據(jù)庫(kù)會(huì)造成非常多重復(fù)不必要的訪問(wèn)。
- 也因?yàn)檫@幾個(gè)問(wèn)題沒(méi)考慮周全廊勃,導(dǎo)致第一版寫(xiě)出的定時(shí)任務(wù)在測(cè)試時(shí)非常的慢懈贺,最開(kāi)始單批執(zhí)行耗時(shí)3秒,到后來(lái)單批耗時(shí)就要1分鐘(分頁(yè)越來(lái)越多)坡垫,我就直接停止測(cè)試開(kāi)始優(yōu)化了梭灿。
優(yōu)化
針對(duì)分頁(yè)的優(yōu)化。如上所述冰悠,如果主鍵是自增或者表里存在某個(gè)字段比如時(shí)間堡妒,那么我們可以通過(guò)分批后添加條件截取數(shù)據(jù)的辦法優(yōu)化,但是我的情況并不允許溉卓。除此自外還有一個(gè)經(jīng)典的優(yōu)化方案皮迟,分頁(yè)時(shí)只查詢主鍵id,再在第二個(gè)function里重新查表得到對(duì)象list桑寨。這樣看起來(lái)是多繞了一點(diǎn)路伏尼,但實(shí)際上mysql主鍵查詢是非常快的尉尾,“覆蓋索引”就是這么個(gè)意思爆阶,查詢條件與結(jié)果都在一個(gè)“索引表”中,所以速度快。優(yōu)化后的分頁(yè)耗時(shí)都在1秒內(nèi)辨图。
針對(duì)緩存的優(yōu)化班套。我在本地做了HashMap緩存(這里遇到了上篇文章的坑,和concurrentHashMap的坑)故河,把能復(fù)用的數(shù)據(jù)都存放在本地吱韭,用空間換時(shí)間,減少重復(fù)的mysql交互鱼的,這部分主要是業(yè)務(wù)相關(guān)的優(yōu)化杉女。
結(jié)果
最終任務(wù)的運(yùn)行時(shí)時(shí)間獲得了巨大優(yōu)化,百萬(wàn)級(jí)數(shù)據(jù)的處理花了4分鐘鸳吸,日常十萬(wàn)級(jí)數(shù)據(jù)只要1分鐘不到,已經(jīng)在可接受范圍內(nèi)了速勇。