1. 程式人生 > 程式設計 >java 定時同步資料的任務優化

java 定時同步資料的任務優化

前言

定時任務在系統中並不少見,主要目的是用於需要定時處理資料或者執行某個操作的情況下,如定時關閉訂單,或者定時備份。而常見的定時任務分為2種,第一種:固定時間執行,如:每分鐘執行一次,每天執行一次。第二種:延時多久執行,就是當發生一件事情後,根據這件時間發生的時間定時多久後執行任務,如:15分鐘後關閉訂單付款狀態,24小時候後關閉訂單並且釋放庫存,而由於第二種一般都是單一資料的處理(主要是指資料量不大,一般情況下只有一個主體處理物件,如:一個訂單以及訂單中的N個商品),所以一般情況下第二種出現效能問題的機率不大(不代表沒有),所以本文主要是針對第一種定時任務來進行優化,而且主要是針對資料同步或者傳遞資料來進行優化,而優化的方式也是根據實際專案中的情況在不同階段進行優化的

第一階段

第一階段屬於原始階段,邏輯也最為簡單,由於同步分為資料同步和傳遞資料,而且2種的需求各不一致(主要是在於是否允許丟失),所以分開分析

第一種型別:傳遞資料

由於傳遞資料可以允許丟失,常見的場景如呼叫憑證推送(常見於介面需要暴露給第三方,為了安全性,可以定時推送呼叫憑證來保證介面安全性),訊息推送(訂單消費成功後推送訊息,由於可能推送失敗,所以需要進入定時任務進行重試,但是因為訊息實時性,所以重試到一定次數後放棄重試)

傳遞資料在第一階段設計非常簡單,定時推送,有限的錯誤次數,同步成功後修改狀態,同步失敗後對失敗次數+1,一旦超過錯誤次數,就不在繼續嘗試

第二種型別:同步資料

同步資料跟傳遞資料不同點在於同步資料一定需要保證資料能投遞成功,否則就要一直進行重試,比如2個系統間的訂單同步,會員資訊同步等

同步資料再第一階段也非常簡單,定時同步資料,失敗就設定同步狀態為同步失敗,每次同步就只查詢狀態為未同步和同步失敗記錄

第二階段

一開始需要傳遞或者同步的系統很少,資料也少,所以沒有什麼問題,但是第二階段不一樣了,資料量稍微有所新增,但是增量不大,主要是需要同步的系統多了,打個比方,連鎖商店,總部需要把資料下傳到所有門店去,這樣門店就不用每次去總部獲取資料,這樣太耗費時間了,當然門店每次從總部獲取到資料可以快取到本地,不過跟本文內容關係不大,所以這裡不再討論。由於需要同步的系統太多,所以延伸出另外一個問題,一旦一個系統的網路環境不好,會影響其他系統資料同步,所以在第二階段,引入了黑名單機制,由於黑名單機制對於傳遞資料和同步資料大致相同,所以這裡就不分開描述,有差異性的地方也會指出

黑名單具體處理機制

黑名單分二級:

第一級用於控制本次定時任務,當本次執行定時任務時,不同的接受資料伺服器可能有0-N條資料需要同步,所以一旦進入第一級黑名單後,本次後面都不會向接受資料伺服器發起請求,而是直接失敗;

第二級用於控制多長時間內不進入重試,是控制整個的,從查詢需要同步的資料時候就直接過濾並且設定為同步失敗狀態(傳遞訊息需要對失敗次數加1)

首先第一級,當請求不到接受資料的伺服器的時候(連結失敗,或者連結超時),會再重試2次(傳遞資料由於及時性要求,所以不會重試,並且超時時間也會合理的減少),如果2次都同步失敗,這判斷本條資料同步失敗,並且進入第一級黑名單,並且判斷一定時間內進入了幾次第一級黑名單,具體使用redis控制,首先是否進入第一級黑名單直接程式中儲存就好,一定時間段內進入了幾次黑名單,就使用有序集合儲存,排序的分值就儲存當前時間戳

進入第一級黑名單後,使用一定時間內進入幾次的限制條件,來判斷是否進入第二級黑名單,比如5分鐘進入3次第一級黑名單,就進入第二級黑名單,那麼就查詢分值大於5分鐘前時間戳的資料集合,如果集合結果有3條或以上資料了,那麼就進入第二級黑名單,同時清理掉redis中關於第一級黑名單儲存的資料,如果沒有3條資料,那麼就刪除分值小於5分鐘前的時間戳的資料,避免垃圾資料過多

使用黑名單機制,可以有效避免一些因為服務本來不可訪問導致一直還重試的問題,並且由於有二級黑名單,所以也一定程度上避免了因為暫時網路波動,導致資料長久無法同步的問題

第三階段

由於需要傳遞的資料和需要同步資料的服務越來越多,並且由於各種問題導致很多資料不能一次性同步成功,所以每次定時任務都需要同步大量資料,這樣就導致及時性很差了,比如幾千條資料同步下來,就算一條只需要幾十毫秒,從開始到最後一條資料同步成功也是幾十秒之後了,所以需要再次對定時任務進行優化,資料量大而導致同步慢原因很簡單,是由於單個執行緒串行同步的,也就是說必須要上一條資料處理了才能處理下一條資料,所以可以使用多執行緒來優化,提高硬體使用率

多執行緒的定時任務

當然肯定不可能給每條資料建立一個執行緒,先不說得建立多少條執行緒,僅僅是建立執行緒的消耗就已經很大了,而且執行緒數量太多,頻繁切換執行緒上下文也會導致效能損耗,所以最合適的就是將資料分配到機器CPU核心數量的執行緒,或者核心數量*2的執行緒上去處理更合適,當然具體情況具體分析,最好還是具體測試得出合適的執行緒數量,同時由於肯定是會存在多個定時任務,所以可以多個定時任務使用同一個執行緒池,但是每個任務只使用合適執行緒數量來處理

執行緒資料分配原則

同一個被接受呼叫的資料的伺服器的資料肯定是分配到一個執行緒中去處理,比如要分配8個執行緒來處理,那麼可以建立8個集合,先儲存查詢出來需要被同步的資料,同時查詢出來的資料根據被接受資料的伺服器標識排序,用接受資料的伺服器標識的hash值來%8來確定放入哪個集合,或者使用輪詢的方式放入指定集合,分配好之後則建立8個runable放入執行緒池中去執行

防止定時任務疊加

開啟多執行緒處理後,由於主執行緒在把任務放入執行緒池中執行的時候就會返回了,所以一定需要防止定時任務疊加,比如任務是10秒執行一次的,每次定時任務本身的執行緒只執行了1秒,下次定時任務的時候會發現定時任務已經處理完成,但是實際上真正同步資料的8個執行緒都沒有執行完成,就會出現一條資料重複同步,或者把資料累加到上次任務的集合中去(看具體的處理方式導致不同的結果),最後就跟滾雪球一樣,整個服務就算不崩潰,也會出現各種問題,或者就是浪費大量資源去做重複同步,所以為了防止任務疊加,需要使用閉鎖來防止定時任務本身返回的情況,同時使用閉鎖也要注意處理異常的情況,防止發生異常後,閉鎖沒有執行操作,導致定時任務一直不能返回

閉鎖

使用閉鎖防止定時任務返回,8個執行緒的情況下建立閉鎖

CountDownLatch latch = new CountDownLatch(8); 

每個執行緒執行完資料後需要countDown方法來通知,或者叫關閉一個柵欄吧,建立閉鎖的傳入的8我們可以看成建立了8個柵欄

latch.countDown(); 

同時在定時任務的執行緒中,需要等待所有柵欄關閉才能繼續執行,所以需要呼叫方法

latch.await(); 

這樣只有所有執行緒執行完成後,定時任務的執行緒才會繼續執行,防止任務疊加

使用多執行緒了,一定要注意多執行緒的一些執行緒安全以及其他的一些問題,如果對閉鎖和多執行緒本身不夠了解的話,可以自行去查閱一些相關資料

第四階段

資料量非常大,接受資料的服務也非常多

一臺伺服器的硬體資源始終有限,尤其是網路資源,由於接受資料的服務不一定是內網服務,加上各種問題導致連結失敗,所以資料量大的情況下,就算使用了多執行緒,還是會造成資料延遲很久才同步成功(主要延遲原因是網路問題),這時候就需要使用多臺伺服器了,而使用多臺伺服器定時執行就存在一個問題,資料分片,簡單來說怎麼保證一條資料只能被一臺伺服器處理,資料分片有2種方式,第一種:不同伺服器處理不同的表的資料。第二種:資料本身主鍵或者某種標誌分配處理

2種處理方式有各自的優缺點

第一種:

優點:簡單,只需要簡單拆分或者配置即可

缺點:無法擴充套件更多,最多隻能可能擴充套件到資料表數量臺伺服器,並且對於熱點資料無法更優處理,比如訂單這些熱點資料,始終都在一臺伺服器

第二種:

優點:理論上可無限擴充套件,可以針對熱點資料專門擴充套件

缺點:配置麻煩,每次新增伺服器需要重新配置

實現分片定時任務

由於第一種配置簡單,而且擴充套件性不強,所以本文主要講述第二種方式的實現;

如果所有資料有生成都有自增型主鍵id,那麼最簡單也最公平的就是給每臺伺服器配置一個從0開始連續的伺服器id,每臺伺服器查詢資料的時候加一個條件id%伺服器臺數=當前伺服器id,注意這樣會導致id列的索引可能無法命中(根據資料庫不同,是否命中情況不一致),這樣配置的好處就是絕對公平,每臺伺服器分配到的資料量是平等的,壞處就是一臺伺服器可能會給所有接受資料服務發起請求,無法更好的利用連結複用,另外也無法針對伺服器配置來增加或者降低權重(當然可以一個伺服器配置2個id的方式來實現,但是這樣也不友好)

如果為了更好的利用連結複用,可以使用先計算出接受資料服務標誌的hashcode值,然後跟進hashcode值%伺服器臺數=當前伺服器id的形式,這樣就可以將接受資料服務分組式的配置到某個伺服器上去處理,當然如果接受資料服務本身存在很大的資料量差異,就不推薦這種方式了,畢竟這樣容易把大量資料堆積到某臺伺服器上去處理

當然還有其他多種分片的配置方式,比如採用表配置的方式來配置哪臺伺服器處理哪些資料,也可以使用上面種方式的結合體,可以根據具體情況分析到底怎麼樣才能更適合的進行資料分片處理,當然常規情況下,採用id%伺服器的臺數是能滿足大部分需求的

其他優化

當系統針對性能優化到一定程度的時候,就可以考慮從業務或者其他方面進行優化了,比如一旦有系統進入二級黑名單了,就發出警告通知,或者沒有進入二級黑名單,但是卻經常進入一級黑名單,也提出一個報警,這樣可以讓人去排查原因,確認是程式問題還是網路本身的問題。另外也可以設定一個閾值,某個接受資料的服務一直響應很慢,或者經常響應時間超過某個閾值的時候,可以考慮進行降權處理,或者排查程式已經網路相關的原因

以上就是java 定時同步資料的任務優化的詳細內容,更多關於Java 定時任務的資料請關注我們其它相關文章!