java 定時同步數(shù)據(jù)的任務(wù)優(yōu)化
定時任務(wù)在系統(tǒng)中并不少見,主要目的是用于需要定時處理數(shù)據(jù)或者執(zhí)行某個操作的情況下,如定時關(guān)閉訂單,或者定時備份。而常見的定時任務(wù)分為2種,第一種:固定時間執(zhí)行,如:每分鐘執(zhí)行一次,每天執(zhí)行一次。第二種:延時多久執(zhí)行,就是當(dāng)發(fā)生一件事情后,根據(jù)這件時間發(fā)生的時間定時多久后執(zhí)行任務(wù),如:15分鐘后關(guān)閉訂單付款狀態(tài),24小時候后關(guān)閉訂單并且釋放庫存,而由于第二種一般都是單一數(shù)據(jù)的處理(主要是指數(shù)據(jù)量不大,一般情況下只有一個主體處理對象,如:一個訂單以及訂單中的N個商品),所以一般情況下第二種出現(xiàn)性能問題的幾率不大(不代表沒有),所以本文主要是針對第一種定時任務(wù)來進(jìn)行優(yōu)化,而且主要是針對數(shù)據(jù)同步或者傳遞數(shù)據(jù)來進(jìn)行優(yōu)化,而優(yōu)化的方式也是根據(jù)實際項目中的情況在不同階段進(jìn)行優(yōu)化的
第一階段第一階段屬于原始階段,邏輯也最為簡單,由于同步分為數(shù)據(jù)同步和傳遞數(shù)據(jù),而且2種的需求各不一致(主要是在于是否允許丟失),所以分開分析
第一種類型:傳遞數(shù)據(jù)由于傳遞數(shù)據(jù)可以允許丟失,常見的場景如調(diào)用憑證推送(常見于接口需要暴露給第三方,為了安全性,可以定時推送調(diào)用憑證來保證接口安全性),消息推送(訂單消費成功后推送消息,由于可能推送失敗,所以需要進(jìn)入定時任務(wù)進(jìn)行重試,但是因為消息實時性,所以重試到一定次數(shù)后放棄重試)
傳遞數(shù)據(jù)在第一階段設(shè)計非常簡單,定時推送,有限的錯誤次數(shù),同步成功后修改狀態(tài),同步失敗后對失敗次數(shù)+1,一旦超過錯誤次數(shù),就不在繼續(xù)嘗試
第二種類型:同步數(shù)據(jù)同步數(shù)據(jù)跟傳遞數(shù)據(jù)不同點在于同步數(shù)據(jù)一定需要保證數(shù)據(jù)能投遞成功,否則就要一直進(jìn)行重試,比如2個系統(tǒng)間的訂單同步,會員信息同步等
同步數(shù)據(jù)再第一階段也非常簡單,定時同步數(shù)據(jù),失敗就設(shè)置同步狀態(tài)為同步失敗,每次同步就只查詢狀態(tài)為未同步和同步失敗記錄
第二階段一開始需要傳遞或者同步的系統(tǒng)很少,數(shù)據(jù)也少,所以沒有什么問題,但是第二階段不一樣了,數(shù)據(jù)量稍微有所新增,但是增量不大,主要是需要同步的系統(tǒng)多了,打個比方,連鎖商店,總部需要把數(shù)據(jù)下傳到所有門店去,這樣門店就不用每次去總部獲取數(shù)據(jù),這樣太耗費時間了,當(dāng)然門店每次從總部獲取到數(shù)據(jù)可以緩存到本地,不過跟本文內(nèi)容關(guān)系不大,所以這里不再討論。由于需要同步的系統(tǒng)太多,所以延伸出另外一個問題,一旦一個系統(tǒng)的網(wǎng)絡(luò)環(huán)境不好,會影響其他系統(tǒng)數(shù)據(jù)同步,所以在第二階段,引入了黑名單機(jī)制,由于黑名單機(jī)制對于傳遞數(shù)據(jù)和同步數(shù)據(jù)大致相同,所以這里就不分開描述,有差異性的地方也會指出
黑名單具體處理機(jī)制黑名單分二級:
第一級用于控制本次定時任務(wù),當(dāng)本次運行定時任務(wù)時,不同的接受數(shù)據(jù)服務(wù)器可能有0-N條數(shù)據(jù)需要同步,所以一旦進(jìn)入第一級黑名單后,本次后面都不會向接受數(shù)據(jù)服務(wù)器發(fā)起請求,而是直接失??;
第二級用于控制多長時間內(nèi)不進(jìn)入重試,是控制整個的,從查詢需要同步的數(shù)據(jù)時候就直接過濾并且設(shè)置為同步失敗狀態(tài)(傳遞消息需要對失敗次數(shù)加1)
首先第一級,當(dāng)請求不到接受數(shù)據(jù)的服務(wù)器的時候(鏈接失敗,或者鏈接超時),會再重試2次(傳遞數(shù)據(jù)由于及時性要求,所以不會重試,并且超時時間也會合理的減少),如果2次都同步失敗,這判斷本條數(shù)據(jù)同步失敗,并且進(jìn)入第一級黑名單,并且判斷一定時間內(nèi)進(jìn)入了幾次第一級黑名單,具體使用redis控制,首先是否進(jìn)入第一級黑名單直接程序中存儲就好,一定時間段內(nèi)進(jìn)入了幾次黑名單,就使用有序集合保存,排序的分值就存儲當(dāng)前時間戳
進(jìn)入第一級黑名單后,使用一定時間內(nèi)進(jìn)入幾次的限制條件,來判斷是否進(jìn)入第二級黑名單,比如5分鐘進(jìn)入3次第一級黑名單,就進(jìn)入第二級黑名單,那么就查詢分值大于5分鐘前時間戳的數(shù)據(jù)集合,如果集合結(jié)果有3條或以上數(shù)據(jù)了,那么就進(jìn)入第二級黑名單,同時清理掉redis中關(guān)于第一級黑名單存儲的數(shù)據(jù),如果沒有3條數(shù)據(jù),那么就刪除分值小于5分鐘前的時間戳的數(shù)據(jù),避免垃圾數(shù)據(jù)過多
使用黑名單機(jī)制,可以有效避免一些因為服務(wù)本來不可訪問導(dǎo)致一直還重試的問題,并且由于有二級黑名單,所以也一定程度上避免了因為暫時網(wǎng)絡(luò)波動,導(dǎo)致數(shù)據(jù)長久無法同步的問題
第三階段由于需要傳遞的數(shù)據(jù)和需要同步數(shù)據(jù)的服務(wù)越來越多,并且由于各種問題導(dǎo)致很多數(shù)據(jù)不能一次性同步成功,所以每次定時任務(wù)都需要同步大量數(shù)據(jù),這樣就導(dǎo)致及時性很差了,比如幾千條數(shù)據(jù)同步下來,就算一條只需要幾十毫秒,從開始到最后一條數(shù)據(jù)同步成功也是幾十秒之后了,所以需要再次對定時任務(wù)進(jìn)行優(yōu)化,數(shù)據(jù)量大而導(dǎo)致同步慢原因很簡單,是由于單個線程串行同步的,也就是說必須要上一條數(shù)據(jù)處理了才能處理下一條數(shù)據(jù),所以可以使用多線程來優(yōu)化,提高硬件使用率
多線程的定時任務(wù)當(dāng)然肯定不可能給每條數(shù)據(jù)創(chuàng)建一個線程,先不說得創(chuàng)建多少條線程,僅僅是創(chuàng)建線程的消耗就已經(jīng)很大了,而且線程數(shù)量太多,頻繁切換線程上下文也會導(dǎo)致性能損耗,所以最合適的就是將數(shù)據(jù)分配到機(jī)器CPU核心數(shù)量的線程,或者核心數(shù)量*2的線程上去處理更合適,當(dāng)然具體情況具體分析,最好還是具體測試得出合適的線程數(shù)量,同時由于肯定是會存在多個定時任務(wù),所以可以多個定時任務(wù)使用同一個線程池,但是每個任務(wù)只使用合適線程數(shù)量來處理
線程數(shù)據(jù)分配原則
同一個被接受調(diào)用的數(shù)據(jù)的服務(wù)器的數(shù)據(jù)肯定是分配到一個線程中去處理,比如要分配8個線程來處理,那么可以創(chuàng)建8個集合,先保存查詢出來需要被同步的數(shù)據(jù),同時查詢出來的數(shù)據(jù)根據(jù)被接受數(shù)據(jù)的服務(wù)器標(biāo)識排序,用接受數(shù)據(jù)的服務(wù)器標(biāo)識的hash值來%8來確定放入哪個集合,或者使用輪詢的方式放入指定集合,分配好之后則創(chuàng)建8個runable放入線程池中去執(zhí)行
防止定時任務(wù)疊加
開啟多線程處理后,由于主線程在把任務(wù)放入線程池中運行的時候就會返回了,所以一定需要防止定時任務(wù)疊加,比如任務(wù)是10秒執(zhí)行一次的,每次定時任務(wù)本身的線程只執(zhí)行了1秒,下次定時任務(wù)的時候會發(fā)現(xiàn)定時任務(wù)已經(jīng)處理完成,但是實際上真正同步數(shù)據(jù)的8個線程都沒有執(zhí)行完成,就會出現(xiàn)一條數(shù)據(jù)重復(fù)同步,或者把數(shù)據(jù)累加到上次任務(wù)的集合中去(看具體的處理方式導(dǎo)致不同的結(jié)果),最后就跟滾雪球一樣,整個服務(wù)就算不崩潰,也會出現(xiàn)各種問題,或者就是浪費大量資源去做重復(fù)同步,所以為了防止任務(wù)疊加,需要使用閉鎖來防止定時任務(wù)本身返回的情況,同時使用閉鎖也要注意處理異常的情況,防止發(fā)生異常后,閉鎖沒有執(zhí)行操作,導(dǎo)致定時任務(wù)一直不能返回
閉鎖
使用閉鎖防止定時任務(wù)返回,8個線程的情況下創(chuàng)建閉鎖
CountDownLatch latch = new CountDownLatch(8);
每個線程執(zhí)行完數(shù)據(jù)后需要countDown方法來通知,或者叫關(guān)閉一個柵欄吧,創(chuàng)建閉鎖的傳入的8我們可以看成創(chuàng)建了8個柵欄
latch.countDown();
同時在定時任務(wù)的線程中,需要等待所有柵欄關(guān)閉才能繼續(xù)執(zhí)行,所以需要調(diào)用方法
latch.await();
這樣只有所有線程執(zhí)行完成后,定時任務(wù)的線程才會繼續(xù)執(zhí)行,防止任務(wù)疊加
使用多線程了,一定要注意多線程的一些線程安全以及其他的一些問題,如果對閉鎖和多線程本身不夠了解的話,可以自行去查閱一些相關(guān)資料
第四階段數(shù)據(jù)量非常大,接受數(shù)據(jù)的服務(wù)也非常多
一臺服務(wù)器的硬件資源始終有限,尤其是網(wǎng)絡(luò)資源,由于接受數(shù)據(jù)的服務(wù)不一定是內(nèi)網(wǎng)服務(wù),加上各種問題導(dǎo)致鏈接失敗,所以數(shù)據(jù)量大的情況下,就算使用了多線程,還是會造成數(shù)據(jù)延遲很久才同步成功(主要延遲原因是網(wǎng)絡(luò)問題),這時候就需要使用多臺服務(wù)器了,而使用多臺服務(wù)器定時執(zhí)行就存在一個問題,數(shù)據(jù)分片,簡單來說怎么保證一條數(shù)據(jù)只能被一臺服務(wù)器處理,數(shù)據(jù)分片有2種方式,第一種:不同服務(wù)器處理不同的表的數(shù)據(jù)。第二種:數(shù)據(jù)本身主鍵或者某種標(biāo)志分配處理
2種處理方式有各自的優(yōu)缺點
第一種:優(yōu)點:簡單,只需要簡單拆分或者配置即可
缺點:無法擴(kuò)展更多,最多只能可能擴(kuò)展到數(shù)據(jù)表數(shù)量臺服務(wù)器,并且對于熱點數(shù)據(jù)無法更優(yōu)處理,比如訂單這些熱點數(shù)據(jù),始終都在一臺服務(wù)器
第二種:優(yōu)點:理論上可無限擴(kuò)展,可以針對熱點數(shù)據(jù)專門擴(kuò)展
缺點:配置麻煩,每次新增服務(wù)器需要重新配置
實現(xiàn)分片定時任務(wù)由于第一種配置簡單,而且擴(kuò)展性不強,所以本文主要講述第二種方式的實現(xiàn);
如果所有數(shù)據(jù)有生成都有自增型主鍵id,那么最簡單也最公平的就是給每臺服務(wù)器配置一個從0開始連續(xù)的服務(wù)器id,每臺服務(wù)器查詢數(shù)據(jù)的時候加一個條件id%服務(wù)器臺數(shù)=當(dāng)前服務(wù)器id,注意這樣會導(dǎo)致id列的索引可能無法命中(根據(jù)數(shù)據(jù)庫不同,是否命中情況不一致),這樣配置的好處就是絕對公平,每臺服務(wù)器分配到的數(shù)據(jù)量是平等的,壞處就是一臺服務(wù)器可能會給所有接受數(shù)據(jù)服務(wù)發(fā)起請求,無法更好的利用鏈接復(fù)用,另外也無法針對服務(wù)器配置來增加或者降低權(quán)重(當(dāng)然可以一個服務(wù)器配置2個id的方式來實現(xiàn),但是這樣也不友好)
如果為了更好的利用鏈接復(fù)用,可以使用先計算出接受數(shù)據(jù)服務(wù)標(biāo)志的hashcode值,然后跟進(jìn)hashcode值%服務(wù)器臺數(shù)=當(dāng)前服務(wù)器id的形式,這樣就可以將接受數(shù)據(jù)服務(wù)分組式的配置到某個服務(wù)器上去處理,當(dāng)然如果接受數(shù)據(jù)服務(wù)本身存在很大的數(shù)據(jù)量差異,就不推薦這種方式了,畢竟這樣容易把大量數(shù)據(jù)堆積到某臺服務(wù)器上去處理
當(dāng)然還有其他多種分片的配置方式,比如采用表配置的方式來配置哪臺服務(wù)器處理哪些數(shù)據(jù),也可以使用上面種方式的結(jié)合體,可以根據(jù)具體情況分析到底怎么樣才能更適合的進(jìn)行數(shù)據(jù)分片處理,當(dāng)然常規(guī)情況下,采用id%服務(wù)器的臺數(shù)是能滿足大部分需求的
其他優(yōu)化當(dāng)系統(tǒng)針對性能優(yōu)化到一定程度的時候,就可以考慮從業(yè)務(wù)或者其他方面進(jìn)行優(yōu)化了,比如一旦有系統(tǒng)進(jìn)入二級黑名單了,就發(fā)出警告通知,或者沒有進(jìn)入二級黑名單,但是卻經(jīng)常進(jìn)入一級黑名單,也提出一個報警,這樣可以讓人去排查原因,確認(rèn)是程序問題還是網(wǎng)絡(luò)本身的問題。另外也可以設(shè)置一個閾值,某個接受數(shù)據(jù)的服務(wù)一直響應(yīng)很慢,或者經(jīng)常響應(yīng)時間超過某個閾值的時候,可以考慮進(jìn)行降權(quán)處理,或者排查程序已經(jīng)網(wǎng)絡(luò)相關(guān)的原因
以上就是java 定時同步數(shù)據(jù)的任務(wù)優(yōu)化的詳細(xì)內(nèi)容,更多關(guān)于Java 定時任務(wù)的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. python中scrapy處理項目數(shù)據(jù)的實例分析2. Django ORM實現(xiàn)按天獲取數(shù)據(jù)去重求和例子3. 快速搭建Spring Boot+MyBatis的項目IDEA(附源碼下載)4. 基于PHP與XML的PDF文檔生成技術(shù)5. Python requests庫參數(shù)提交的注意事項總結(jié)6. php調(diào)用mysql存儲過程和函數(shù)的方法7. 教你在 IntelliJ IDEA 中使用 VIM插件的詳細(xì)教程8. AJAX的跨域問題解決方案9. js抽獎轉(zhuǎn)盤實現(xiàn)方法分析10. 如何完全清理你的Docker數(shù)據(jù)
