實現一個刷數任務,需要思考哪些維度?
來源:撿田螺的小男孩
前言
大家好,我是田螺。
相信很多後端開發的夥伴們,都做過刷數任務了吧。今天跟大家聊聊,做好一個刷數任務,需要具備哪些後端思維。
1. 資料的備份和還原
我們做刷數任務的時候,首先要考慮的是,這些被刷的資料是否還要還原的。或者刷出問題時,需要回滾的。如果是的話,我們就要做好備份。
如果你是把資料遷移到新的表,則有可能不需要備份,這個具體問題具體分析的哈。
通常,我們在一個事務內,先備份資料,再操作刷數邏輯。
當然,備份資料的方式有多種方式,可以資料庫備份,比如搞一個備份表。或者檔案系統快照等,在需要的時候,就還原資料。
2.刷數維度是什麼?是否支援灰度?
我們刷數的時候,先確認下具體的業務需求和資料模型。然後需要確定刷數的維度是什麼。
客戶維度:比如刷數的維度可以是一個客戶。刷數的時候,把這個客戶的所有資料撈取出來,在一個事務內,把客戶的所有資料,按照業務的規則,刷到一個表。 賬號維度:刷數的維度是賬號,在執行刷數的時候,就是把這個賬號的所有資料撈取出來,在一個事務內,把賬號的所有資料,刷到另外一個表。
當然,還有其他維度,比如產品維度等等都可以,就看業務需求和你們的資料模型。
確認了刷數維度後,需要思考你的刷數是否支援灰度。比如確認客戶維度刷數後,你實現的程式碼,是否支援灰度刷數,也就是說,是否支援先刷一部分客戶,確認沒問題後,再根據配置刷全量客戶。
3. 併發考慮,刷數過程是否需要加鎖。
比如,你要給一個客戶相關的業務表刷數,需要考慮併發場景,簡單點說,就是你在刷數的過程中,客戶是否可能在做交易請求,這時候,是否可能產生髒資料。
一般情況下,可以考慮給客戶維度加分散式鎖,比如加鎖的key是customerId
。但是我們加鎖的時候,肯定是不希望影響客戶太久的,因為加鎖後,整個刷數的過程中,系統是處理不了這個使用者的交易請求的,也就是說影響交易了。
所以也一般刷數任務,我們會選擇在夜深人靜的時候執行,這時候使用者發生交易的機率很低,影響相對較小。
4. 刷數失敗怎麼辦?是否支援重試?
我們在刷數的時候,有可能會刷失敗。比如因為網路問題、或者目標表結構等等。失敗後,我們有哪些措施保證呢:
如果刷數失敗的話,我們要確保資料的完整性和一致性,一般一個刷數維度,需要加事務,確保失敗可以回滾,保證資料一致性。刷失敗之後,首先確認分散式鎖要被移除(如果有加鎖的話),因為要確認即使失敗後,交易也是能正常進行的。
還要考慮失敗支援重試。可以自動重試或者手動重試,比如透過xxl-job
定時任務撈取,繼續重試。有時候,可以設定重試次數和重試間隔,確保任務在一段時間內嘗試恢復。
5. 恰到好處的事務處理
我們第4小節提到,如果失敗了,需要保證資料的完整性和一致性。其實,一般我們刷數,就是透過加事務處理去保證的。
事務需要加到恰到好處,比如我們不能所有的刷數業務都放到一個事務內。如果我們是按照客戶維度來刷數的,我們就一個事務把一個客戶所有的資料刷的邏輯放到一塊,當然,有些查詢是可以放到事務外處理的,一些更新或者修改、刪除操作則放到事務內。
如果你的資料是分庫處理的,則有可能刷數的時候要考慮分散式事務了。
6. 效能最佳化,考慮多線並行執行。
如果我們的刷數任務資料量很大,執行耗時比較久的話。就建議可以多執行緒並行執行。
比如你是分庫分表的,是有30個表,你可以A執行緒執行1-10的表,B執行緒執行11-20的表,C執行緒執行21-30的表。
7. 日誌記錄
我們執行一個刷數任務,一定要做好日誌記錄。
我記得我們技術領導說過一句話,很有道理:評價你的日誌是否列印得是否夠好。就是你根據控制檯列印出的日誌,能知道你的複雜業務執行到什麼流程。如果異常中斷,你能根據日誌快速知道什麼異常,哪個業務資料有問題,那就夠了。
刷數日誌列印,一般包括:
記錄詳細的刷數任務日誌 包括執行的步驟 刷數成功與否 刷數耗時等資訊
8.監控告警
我們開發刷數邏輯的時候,如果某種返回不符合預期的時候,就需要告警上報監控(比如插入資料庫返回跟預期插入條數不一致)。
又或者是你刷數失敗,需要包這個異常日誌列印出來,並且上報監控(比如普羅米修斯,和企業微信通知)。比如這塊程式碼:
try{
flushService.flushDataCustomerLevel(customerNo);
}catch(Exception e){
Logger.error("flush customer data fail: {}", customerNo, e);
prometheusMonitor.report("刷數失敗",customerNo);
notify();
weChatWorkSend();
}
9. 資料量大的時候,最好壓測
如果你刷的資料量很大的時候,最好做壓測。壓測通常包括模擬多種負載情況,以確保系統在不同條件下都能正常執行。
做刷數任務壓測,主要考慮這幾方面:
資料量:使用大量資料進行測試,以確保系統在處理大規模資料時的效能。這可以包括資料量的增長、查詢和寫入的吞吐量等。 網路延遲和吞吐量:在模擬網路延遲和限制吞吐量的情況下進行測試,以瞭解系統在這些條件下的表現。 錯誤處理:引入模擬的錯誤場景,例如資料庫連線中斷、請求超時等,以驗證系統對錯誤的處理能力。
刷數壓測的好處:
效能評估:壓測可以幫助評估系統在處理大量資料時的效能。透過模擬真實負載,可以更好地瞭解系統的響應時間、吞吐量和資源利用情況。 容量規劃:透過壓測,可以更好地瞭解系統在不同負載條件下的容量需求。這有助於進行容量規劃,確保系統能夠應對未來的增長。 發現潛在問題:壓測可以幫助發現系統中可能存在的潛在問題,如記憶體洩漏、併發問題、資源瓶頸等。透過在模擬環境中發現並解決這些問題,可以避免它們在生產環境中引起嚴重後果。 驗證容錯能力:壓測可以測試系統在出現錯誤、超時、網路故障等異常條件下的容錯能力。這有助於確保系統能夠適應不可預測的情況。 效能最佳化:壓測結果提供了效能瓶頸的線索,幫助團隊進行效能最佳化。透過分析效能資料,可以識別需要改進的部分並最佳化系統。 規遇風險:透過壓測,可以更早地發現潛在的效能問題和瓶頸,有助於規遇系統上線後可能面臨的風險。
10. 實戰壓軸彙總:做一個刷數任務,如何更好保護你的系統
10.1 設定個配置時間,可以控制任務跑多長時間後終止。
有些時候,我們如果擔心刷數任務跑太久,可能會影響交易,這時候我們可以搞個配置變數,比如apollo配置變數,控制刷數多長時間後,可以停止。
10.2 迴圈分頁查詢,設定最大次數告警監控
我們做刷數任務的時候,經常是分頁迴圈掃描某個客戶/使用者表,然後一批一批出來執行刷數邏輯。比如虛擬碼像這樣:
long minId
while(true){
List<CustomerDo> customerList = customer.pageQueryAscID(pageSize,minId);
flushCustomerData(customerList);
if(customerList.size()< pageSize){
break;
}
minId = customerList.get(customerList.size()-1).getId();
}
這塊程式碼,其實沒啥問題。有些時候,我們可能手抖寫錯了,可能導致死迴圈。
其實為了保護我們的系統,我們可以先確認下客戶有多少,然後設定個迴圈次數,當超過最大迴圈次數之後,就告警排查確認。
long minId;
Integer maxCycleNum =1000;
Integer cycleNum = 0;
while(true ){
List<CustomerDo> customerList = customer.pageQueryAscID(pageSize,minId);
flushCustomerData(customerList);
if(customerList.size()< pageSize){
break;
}
minId = customerList.get(customerList.size()-1).getId();
if(cycleNum>= maxCycleNum){
//告警
}
cycleNum ++;
}
當然這只是個一種後端思路哈。
10.3 如果定時任務是xxl-job ,路由規則是什麼,併發問題考慮
大家如果使用過xxl-job作為定時任務,應該抖配置過它的路由規則吧。比如是分片的,還是第一個/最後一個等等。
如果是分片的,就是多個pod都可能執行到你的業務邏輯。這時候你要考慮併發執行,你的業務是否收影響。
10.4 SQL是否命中索引,是否存在慢SQL
我們做刷數任務的時候,很多時候,都要跟SQL打交道。
我們要確保查詢、更新、或者刪除的資料量大的表,都要有索引了。要確保沒有慢SQL。
常規的我們可以用explain分析SQL,我們還可以透過壓測分析出來。
10.5 如果加了分散式鎖,鎖時間考慮
如果你是按照客戶維度刷數,加了客戶維度的分散式鎖,你要考慮鎖時間是多久,鎖時間是否可以配置(一般這種最好配置一下。)
如果你時間設定小,那這時候刷數還沒完成,鎖就超時釋放了,那不就有問題啦。
如果你時間設定過長也不太好,當然,你在刷完數,finally執行釋放鎖也可以。
finally {
redisService.deleteKey(customerNoKey);
}
10.6 大事務考慮,事務是否太多?是否可以拆分為小事務
我們在刷數的時候,為了保證資料的完整性和一致性,一般要求加事務的。
但是,切忌事務不要太大,我們可以把一些查詢放到事務外,把計算邏輯也放事務外,把資料庫的更新、新增、刪除操作放到事務內就好。
就是把大事務拆分為小事務。
10.7 列印耗時時間,如果時間夠長
一般來說,做刷數,儘量列印一下耗時,這樣我們可以根據日誌,觀察是否有哪些問題需要及時處理的。
比如列印刷一個客戶要多久。或者列印一批客戶要多久,等等。
10.8 是否可以加個配置,減少掃描
有些時候,我們需要配置一定的灰度規則來支援灰度刷數。如果刷數流程是先掃描所有客戶,然後接著判斷客戶是否命中灰度。這樣每次任務執行,都會掃描客戶表。
我麼可以考慮加個配置,傳特定的客戶號,根據客戶號列表,去查客戶列表,然後開始刷數邏輯,不用再全表掃描客戶表了。
10.9 是否考慮校驗邏輯
有些時候,我們沒法確認我們刷的邏輯是否正確,這時候,可以考慮是否加校驗邏輯。
你可以非同步進行校驗,也可以同步校驗(當然,如果耗時不大的時候)
10.10 try...catch 包住可能的異常
如果我們是按照一個客戶維度去刷數的,你要確認A
客戶刷失敗,是不是不能影響B
客戶。這時候建議try...catch
包住可能的異常,這樣即有利於分析錯誤原因,又可以不不因為未知異常導致刷數中斷。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024922/viewspace-3009050/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- linux實現一個定時任務Linux
- 實現一個“計劃任務”機制
- 實現一個併發任務執行框架框架
- 一個任務代辦的定時提醒應該需要掌握哪些知識點?
- 接到需求任務,我們需要做哪些事情?
- 一個延時任務問題引發的思考
- crontab+shell 實現每秒執行一個任務
- 如何實現一個TCC分散式事務框架的一點思考分散式框架
- oracle實習的第一個任務Oracle
- 回首五年運維,運維需要思考運維
- 在Dataphin 排程運維時,針對週期任務,我們需要配置哪些關鍵資訊?運維
- 微服務思考(02):微服務實施前有哪些問題?微服務
- 實驗任務四:登入介面、實驗任務五:猜數字
- 幾種實現延時任務的方式(一)
- MemQ 實現非同步任務MQ非同步
- 定時任務的實現
- 實現一個任務排程系統,看這篇文章就夠了
- 怎樣實現一個非阻塞的超時重試任務佇列佇列
- 【UWP】實現一個波浪進度條
- 專案任務與運維任務的衝突運維
- 【IT運維】Linux運維需要掌握哪些技能?運維Linux
- Java如何實現定時任務?Java
- 封裝一個阻塞佇列,輕鬆實現排隊執行任務功能!封裝佇列
- “遊戲評論”的另一個維度:抽象與真實遊戲抽象
- 一個HTTP需要經過哪些步驟HTTP
- 如何寫一個任務佇列佇列
- 基於Hyperf開發的任務排程系統.支援任務投遞,DAG任務編排(多個任務使用同一個事務).
- 實施微服務,我們需要哪些基礎框架?微服務框架
- 實現PMC的數字化轉型需要哪些具體的步驟?
- Oracle自動維護任務Oracle
- crontab定時任務維護
- 直播系統原始碼,實現倒數計時,定時任務原始碼
- 網站流量前SEO需要思考哪些問題?網站
- 任務排程的思考和總結
- 使用 Promise 實現任務佇列傳送請求,實現最大請求數目限制Promise佇列
- 自動化平臺中維度設計的一點思考
- 任務一
- 任務:重現